Compare commits

...

261 Commits

Author SHA1 Message Date
Sercan Yemen
d25ce3601a Merge branch 'master' into skeleton 2018-07-12 17:46:33 +03:00
Sercan Yemen
ffbd000fe2 (Chat Panel) Style and logic tweaks 2018-07-12 17:45:46 +03:00
Sercan Yemen
b6ebd2b839 (Navbar) Fixed: IE11 issues
(Chat Panel) Fixed: IE11 issues
2018-07-12 16:04:56 +03:00
Sercan Yemen
4d4d52ba05 (Navbar) Fixed: Style variant 2 doesn't scroll on Firefox, IE & Edge
(Chat Panel) Fixed: Doesn't scroll on Firefox, IE & Edge
Increased Fuse version
2018-07-12 15:48:55 +03:00
Sercan Yemen
251732f221 Updated changelog 2018-07-12 13:59:51 +03:00
Sercan Yemen
4642b730cb Merge branch 'master' into skeleton 2018-07-12 13:52:29 +03:00
Sercan Yemen
0982d5369b Merge branch 'master' into skeleton 2018-07-12 13:47:38 +03:00
Sercan Yemen
1c34a65034 Increased Fuse version 2018-07-12 13:30:17 +03:00
Sercan Yemen
b8803a055f Removed side panel from auth pages
Updated the changelog
Theme options button adjustments
2018-07-12 13:27:26 +03:00
Sercan Yemen
6b8cd41d5e Added SidePanel to the layouts for controlling the chat bar and possible other bar properties via FuseConfig 2018-07-12 13:19:34 +03:00
Sercan Yemen
fe7fdf0a00 Merge branch 'master' into skeleton 2018-07-12 11:40:11 +03:00
Sercan Yemen
2c7ef4de00 Updated Fuse version and changelog 2018-07-12 11:38:02 +03:00
Sercan Yemen
adc93d5d40 (AppComponent) Theme options panel button position 2018-07-12 11:16:41 +03:00
Sercan Yemen
77f6062a55 Merge branch 'master' into skeleton 2018-07-12 11:13:59 +03:00
Sercan Yemen
fdb572fadd (Toolbar) Removed unnecessary chat panel toggle method 2018-07-12 11:13:09 +03:00
Sercan Yemen
cef9e8a9c0 Removed chat panel from Skeleton since its more like an app and won't be required by everyone 2018-07-12 11:12:03 +03:00
Sercan Yemen
2f0d1e406f Merge branch 'master' into skeleton 2018-07-12 11:09:48 +03:00
Sercan Yemen
528c3f95e7 (Navbar) Fixed: Navbar doesn't grow if it has a small content 2018-07-12 11:04:54 +03:00
Sercan Yemen
f395046945 (Chat Panel) Removed mat-list, moved mat-tooltip to the avatar due to mobile scrolling issues 2018-07-12 10:48:31 +03:00
Sercan Yemen
b099022f5a (FuseSidebar) Max. width & width tweaks for small screen devices 2018-07-12 10:20:34 +03:00
Sercan Yemen
25a6ca2684 (Navbar) Added classes to the navbar containing fuse-sidebar's to contain their custom styles 2018-07-12 09:25:56 +03:00
Sercan Yemen
0cd5d613e0 (AppComponent) Added missing 'is-mobile' control - Fixes: Visible scrollbars on some mobile devices 2018-07-12 09:24:54 +03:00
Sercan Yemen
f45ad11861 Updated changelog date 2018-07-11 11:03:37 +03:00
Sercan Yemen
b7ab5ea273 Merge branch 'master' into skeleton 2018-07-11 10:51:24 +03:00
Sercan Yemen
d7e65460bf Merge branch 'master' into skeleton 2018-07-11 10:32:37 +03:00
Sercan Yemen
3cbe302b54 (Toolbar) Changed the username + Small margin tweaks
(Navbar) Style variant 2 adjustments and tweaks
2018-07-11 10:29:21 +03:00
Sercan Yemen
e1c906f08b (Chat Panel) Don't unfold the panel on hover
(Chat Panel) Always keep the contacts list in view
2018-07-10 14:55:30 +03:00
Sercan Yemen
585709cf93 (FuseSidebar) Exported temporary fold & unfold methods
(FuseSidebar) Added [foldedAutoTriggerOnHover] input for disabling the fold/unfold on mouseenter/mouseleave
(Chat Panel) Don't unfold the panel on hover
(Chat Panel) Always keep the contacts list in view
2018-07-10 14:55:08 +03:00
Sercan Yemen
ac70ecc616 (Navbar) Improved the style variant 1
Updated Changelog
2018-07-10 13:47:58 +03:00
Sercan Yemen
d5b64f3258 Updated Angular Material to 6.3.3 2018-07-10 12:26:55 +03:00
Sercan Yemen
59d838ef51 Updated changelog 2018-07-10 12:26:40 +03:00
Sercan Yemen
8b14366763 (Angular Material Examples) Updated the examples 2018-07-10 12:26:29 +03:00
Sercan Yemen
23b86a7e3d (Navbar) Style1 variant folded height 2018-07-10 12:26:04 +03:00
Sercan Yemen
2f497f1c7b (Navbar) Improved navbar style1 variant 2018-07-10 11:55:04 +03:00
Sercan Yemen
18009c9275 (Navbar) Finished navbar variants 2018-07-09 21:29:15 +03:00
Sercan Yemen
20d5a68bf3 (Colors) Further improved the color generators + added adaptive-border-color class 2018-07-09 16:37:00 +03:00
Sercan Yemen
59d53ba0b9 (Colors) Improved the color generators 2018-07-09 16:29:46 +03:00
Sercan Yemen
a6c91dd744 (AngularCLI) Added a configuration to serve with extractCss and sourceMap enabled 2018-07-09 14:31:11 +03:00
Sercan Yemen
5045482ef5 (Navbar) Started to adding new style variants to the navbar (wip) 2018-07-09 14:09:53 +03:00
Sercan Yemen
c7d9a7808a (Footer) Updated buy button link 2018-07-06 10:55:18 +03:00
Sercan Yemen
e4c0340cd7 (Sidebar) Import the takeUntil from the correct location 2018-07-04 19:32:24 +03:00
Sercan Yemen
67cb05be6b (Chat) Replaced multi line comments with single line comments 2018-07-04 17:55:52 +03:00
Sercan Yemen
634ff42f1a (Navigation) Updated version number badge 2018-07-04 17:50:37 +03:00
Sercan Yemen
0149beb33c (Chat Panel) Small color tweak on logo
Updated Angular Material to 6.3.2
Updated the changelog
Increased the Fuse version to 6.2.0 due to the amount of new stuff
2018-07-04 17:49:44 +03:00
Sercan Yemen
42d9748b10 (Chat Panel) Changed the chat panel icon and title 2018-07-04 17:29:44 +03:00
Sercan Yemen
8355e8a17c Updated changelog 2018-07-04 17:26:25 +03:00
Sercan Yemen
dbb925334a (Chat Panel) Styling adjustments 2018-07-04 17:21:51 +03:00
Sercan Yemen
49c8e32dce (Chat Panel) Styling adjustments 2018-07-03 21:05:52 +03:00
Sercan Yemen
595b16275b (Chat Panel) Added a chat panel 2018-07-03 18:23:51 +03:00
Sercan Yemen
fb003bc96f (Scrumboard) Removed an extra semi-colon 2018-07-03 18:06:33 +03:00
Sercan Yemen
d781e59928 (Sidebar) Added "foldedWidth" input for controlling the width of the folded sidebar
(Sidebar) Replaced the margin with padding on the folded sidebar's sibling
(Docs) Updated Sidebar docs
2018-07-03 13:54:19 +03:00
Sercan Yemen
89f71735a8 (Footer) Make the footer fixed for the Demo so the links can be seen at all times 2018-07-03 13:51:31 +03:00
Sercan Yemen
51d7c6fd6f (Contacts) Make the "Add Contact" button sticky on mobile devices 2018-07-02 19:51:37 +03:00
Sercan Yemen
e182f19644 Fix scss import path 2018-07-02 19:48:48 +03:00
Sercan Yemen
d4d57480a1 Merge branch 'master' into skeleton 2018-07-02 16:47:30 +03:00
Sercan Yemen
a1aed2998d (Navbar) Small fix for "scroll the active menu item into the view" 2018-07-02 16:46:55 +03:00
Sercan Yemen
0d6d08aa85 Merge branch 'master' into skeleton 2018-07-02 15:59:00 +03:00
Sercan Yemen
7ff1d2aed0 (Navbar) Scroll the active menu item into the view 2018-07-02 15:58:07 +03:00
Sercan Yemen
09d1b1034e (Fuse) Tree-shakable core services, closes #64 2018-07-02 14:30:18 +03:00
Sercan Yemen
4e98ab1682 (Search Bar) Fixed: Height adjustment happens on the wrong media step 2018-07-02 13:29:33 +03:00
Sercan Yemen
984004d07a (Toolbar) Fixed: Unnecessary _.find in setLanguage method, Closes #66
(Navigation) Fixed: Removed id causing a style issue
2018-07-02 13:22:14 +03:00
Sercan Yemen
64e0451dc6 Removed the id from the navigation as there might be multiple navigations
Fixed: getFlatNavigation doesn't correctly get the 'collapsable' items because of the item.type change
Closes #71: getFlatNavigation method not correctly working when its called multiple times
2018-07-02 13:15:49 +03:00
Sercan Yemen
224bbf479a Fixed the Loading bar service small issue 2018-07-02 11:42:03 +03:00
Sercan Yemen
1aa79c257b Updated the changelog 2018-07-01 14:06:41 +03:00
Sercan Yemen
8e5fdb1d31 Merge branch 'master' into skeleton 2018-07-01 14:03:23 +03:00
Sercan Yemen
4d93b6acef Added a service for the Loading Bar
Added FuseLoadingBarService docs
Renamed service docs files
2018-07-01 14:02:14 +03:00
Sercan Yemen
2eb952e9bc Merge branch 'master' into skeleton 2018-07-01 13:16:15 +03:00
Sercan Yemen
ae29f1f03d Added 'openedChanged' and 'foldedChanged' events to the sidebar
Updated the Sidebar docs
2018-07-01 13:10:11 +03:00
Sercan Yemen
f35c1add1c Added ability to add custom classes to the navigation items using the "classes" property
Fixed: Item function and External URL's are missing from horizontal navigation
Fixed: Horizontal navigation active item does not highlight correctly
2018-07-01 12:20:33 +03:00
Sercan Yemen
b418049d94 Merge branch 'master' into skeleton 2018-06-29 16:58:25 +03:00
Sercan Yemen
388b724e90 Revert version number increase 2018-06-29 16:58:00 +03:00
Sercan Yemen
34d77c1d1a Merge branch 'master' into skeleton 2018-06-28 21:00:37 +03:00
Sercan Yemen
023bfea4df Removed the types definition for Webstorm IDE compatibility 2018-06-28 20:59:55 +03:00
Sercan Yemen
fd4da1e060 Simplified the class and the template as we don't need an extra object for errors
Used native .hasError checks on template
2018-06-28 20:34:43 +03:00
Sercan Yemen
85226e6094 Fixed: Changing 'password' field while 'passwordConfirm' field is filled doesn't trigger the 'confirmPassword' validator
Simplified the class and the template as we don't need an extra object for errors
Used native .hasError checks on template
2018-06-28 20:11:36 +03:00
Sercan Yemen
723c289a47 Merge branch 'master' into skeleton 2018-06-28 17:39:38 +03:00
Sercan Yemen
26a7cc41de Fixed broken print styles due to the latest layout updates 2018-06-28 17:39:22 +03:00
Sercan Yemen
23f8547be5 Merge branch 'master' into skeleton 2018-06-28 13:29:15 +03:00
Sercan Yemen
3deba51322 Updated Angular & Angular Material
Increase the Fuse version number
Fixed the HMR serve ('npm run start-hmr' and 'npm run start-hmr-sourcemaps')
2018-06-28 13:28:56 +03:00
Sercan Yemen
b54bbc8abe Merge branch 'master' into skeleton 2018-06-20 17:08:19 +03:00
Sercan Yemen
1835c060d6 Updated the package-lock.json 2018-06-20 17:07:06 +03:00
Sercan Yemen
f75d458abe Added changelog for v6.1.2 2018-06-20 16:37:59 +03:00
Sercan Yemen
6320e98938 Added sticky header to the e-commerce tables 2018-06-20 16:36:31 +03:00
Sercan Yemen
94d20f8d8d Replace cdkTable stuff with matTable since table now is a proper Material element 2018-06-20 16:32:26 +03:00
Sercan Yemen
ba2f50bf62 Fixed Angular Material datepicker examples 2018-06-20 16:29:28 +03:00
Sercan Yemen
13746c2a73 Updated Angular Material examples 2018-06-20 16:16:03 +03:00
Sercan Yemen
faef6ec6f8 Increased the Fuse version number
Updated Angular Material and couple other packages
2018-06-20 15:30:54 +03:00
Sercan Yemen
b46b253c1c Small responsive fixes on e-commerce pages 2018-06-18 18:53:21 +03:00
Sercan Yemen
3abd764715 Updated the changelog 2018-06-18 18:42:35 +03:00
Sercan Yemen
1acfe2dfb3 Update the version on changelog navigation item 2018-06-17 14:44:11 +03:00
Sercan Yemen
41a93857b6 Merge branch 'master' into skeleton 2018-06-17 14:30:19 +03:00
Sercan Yemen
6d3cfe8ace Increase the version number
Updated the changelog
2018-06-17 14:29:35 +03:00
Sercan Yemen
147525d16a (Sidebar) Fixed the folded issue 2018-06-17 14:24:10 +03:00
Sercan Yemen
29f8ddda9e Remove unnecessary input binding from sidebars 2018-06-17 13:16:04 +03:00
Sercan Yemen
1f77e201a1 Added the update date to the changelog 2018-06-15 19:46:31 +03:00
Sercan Yemen
0e2dca00c0 Merge branch 'master' into skeleton 2018-06-15 18:42:25 +03:00
Sercan Yemen
c93cecdddd Merge branch 'master' into skeleton
# Conflicts:
#	angular.json
#	src/app/app.module.ts
#	src/app/fake-db/academy.ts
#	src/app/fake-db/calendar.ts
#	src/app/fake-db/chat.ts
#	src/app/fake-db/contacts.ts
#	src/app/fake-db/dashboard-analytics.ts
#	src/app/fake-db/dashboard-project.ts
#	src/app/fake-db/e-commerce.ts
#	src/app/fake-db/fake-db.service.ts
#	src/app/fake-db/faq.ts
#	src/app/fake-db/file-manager.ts
#	src/app/fake-db/icons.ts
#	src/app/fake-db/invoice.ts
#	src/app/fake-db/knowledge-base.ts
#	src/app/fake-db/mail.ts
#	src/app/fake-db/profile.ts
#	src/app/fake-db/quick-panel.ts
#	src/app/fake-db/scrumboard.ts
#	src/app/fake-db/search.ts
#	src/app/fake-db/todo.ts
#	src/app/main/angular-material-elements/angular-material-elements.component.html
#	src/app/main/angular-material-elements/angular-material-elements.component.scss
#	src/app/main/angular-material-elements/example-components.ts
#	src/app/main/angular-material-elements/example-viewer/example-viewer.html
#	src/app/main/angular-material-elements/example-viewer/example-viewer.scss
#	src/app/main/angular-material-elements/material.module.ts
#	src/app/main/apps/academy/academy.module.ts
#	src/app/main/apps/academy/course.service.ts
#	src/app/main/apps/academy/course/course.component.scss
#	src/app/main/apps/academy/courses.service.ts
#	src/app/main/apps/academy/courses/courses.component.html
#	src/app/main/apps/academy/courses/courses.component.scss
#	src/app/main/apps/calendar/calendar.component.html
#	src/app/main/apps/calendar/calendar.component.scss
#	src/app/main/apps/calendar/calendar.component.ts
#	src/app/main/apps/calendar/calendar.module.ts
#	src/app/main/apps/calendar/calendar.service.ts
#	src/app/main/apps/calendar/event-form/event-form.component.html
#	src/app/main/apps/calendar/event-form/event-form.component.scss
#	src/app/main/apps/calendar/event-form/event-form.component.ts
#	src/app/main/apps/calendar/event.model.ts
#	src/app/main/apps/chat/chat-start/chat-start.component.scss
#	src/app/main/apps/chat/chat-start/chat-start.component.ts
#	src/app/main/apps/chat/chat-view/chat-view.component.html
#	src/app/main/apps/chat/chat-view/chat-view.component.scss
#	src/app/main/apps/chat/chat.component.html
#	src/app/main/apps/chat/chat.component.scss
#	src/app/main/apps/chat/chat.service.ts
#	src/app/main/apps/chat/sidenavs/left/chats/chats.component.html
#	src/app/main/apps/chat/sidenavs/left/chats/chats.component.scss
#	src/app/main/apps/chat/sidenavs/left/left.component.scss
#	src/app/main/apps/chat/sidenavs/left/user/user.component.html
#	src/app/main/apps/chat/sidenavs/left/user/user.component.scss
#	src/app/main/apps/chat/sidenavs/right/contact/contact.component.html
#	src/app/main/apps/chat/sidenavs/right/contact/contact.component.scss
#	src/app/main/apps/chat/sidenavs/right/right.component.scss
#	src/app/main/apps/contacts/contact-form/contact-form.component.html
#	src/app/main/apps/contacts/contact-form/contact-form.component.scss
#	src/app/main/apps/contacts/contact-form/contact-form.component.ts
#	src/app/main/apps/contacts/contact-list/contact-list.component.html
#	src/app/main/apps/contacts/contact-list/contact-list.component.scss
#	src/app/main/apps/contacts/contact.model.ts
#	src/app/main/apps/contacts/contacts.component.html
#	src/app/main/apps/contacts/contacts.service.ts
#	src/app/main/apps/contacts/selected-bar/selected-bar.component.html
#	src/app/main/apps/contacts/selected-bar/selected-bar.component.scss
#	src/app/main/apps/contacts/sidebars/main/main.component.html
#	src/app/main/apps/contacts/sidebars/main/main.component.scss
#	src/app/main/apps/dashboards/analytics/analytics.component.html
#	src/app/main/apps/dashboards/analytics/analytics.component.scss
#	src/app/main/apps/dashboards/analytics/analytics.component.ts
#	src/app/main/apps/dashboards/analytics/analytics.module.ts
#	src/app/main/apps/dashboards/analytics/analytics.service.ts
#	src/app/main/apps/dashboards/project/project.component.ts
#	src/app/main/apps/dashboards/project/project.module.ts
#	src/app/main/apps/dashboards/project/project.service.ts
#	src/app/main/apps/e-commerce/dashboard/dashboard.component.html
#	src/app/main/apps/e-commerce/dashboard/dashboard.component.scss
#	src/app/main/apps/e-commerce/dashboard/dashboard.component.ts
#	src/app/main/apps/e-commerce/dashboard/dashboard.service.ts
#	src/app/main/apps/e-commerce/e-commerce.module.ts
#	src/app/main/apps/e-commerce/order/order-statuses.ts
#	src/app/main/apps/e-commerce/order/order.component.html
#	src/app/main/apps/e-commerce/order/order.component.scss
#	src/app/main/apps/e-commerce/order/order.model.ts
#	src/app/main/apps/e-commerce/order/order.service.ts
#	src/app/main/apps/e-commerce/orders/orders.component.html
#	src/app/main/apps/e-commerce/orders/orders.component.scss
#	src/app/main/apps/e-commerce/orders/orders.service.ts
#	src/app/main/apps/e-commerce/product/product.component.html
#	src/app/main/apps/e-commerce/product/product.component.scss
#	src/app/main/apps/e-commerce/product/product.model.ts
#	src/app/main/apps/e-commerce/product/product.service.ts
#	src/app/main/apps/e-commerce/products/products.component.html
#	src/app/main/apps/e-commerce/products/products.component.scss
#	src/app/main/apps/e-commerce/products/products.service.ts
#	src/app/main/apps/file-manager/file-list/file-list.component.html
#	src/app/main/apps/file-manager/file-list/file-list.component.scss
#	src/app/main/apps/file-manager/file-manager.service.ts
#	src/app/main/apps/file-manager/sidebars/details/details.component.html
#	src/app/main/apps/file-manager/sidebars/details/details.component.scss
#	src/app/main/apps/file-manager/sidebars/main/main.component.html
#	src/app/main/apps/file-manager/sidebars/main/main.component.scss
#	src/app/main/apps/file-manager/sidebars/main/main.component.ts
#	src/app/main/apps/mail-ngrx/dialogs/compose/compose.component.html
#	src/app/main/apps/mail-ngrx/dialogs/compose/compose.component.scss
#	src/app/main/apps/mail-ngrx/i18n/en.ts
#	src/app/main/apps/mail-ngrx/i18n/tr.ts
#	src/app/main/apps/mail-ngrx/mail-details/mail-details.component.html
#	src/app/main/apps/mail-ngrx/mail-details/mail-details.component.scss
#	src/app/main/apps/mail-ngrx/mail-list/mail-list-item/mail-list-item.component.html
#	src/app/main/apps/mail-ngrx/mail-list/mail-list-item/mail-list-item.component.scss
#	src/app/main/apps/mail-ngrx/mail-list/mail-list-item/mail-list-item.component.ts
#	src/app/main/apps/mail-ngrx/mail-list/mail-list.component.html
#	src/app/main/apps/mail-ngrx/mail-list/mail-list.component.scss
#	src/app/main/apps/mail-ngrx/mail.component.scss
#	src/app/main/apps/mail-ngrx/mail.model.ts
#	src/app/main/apps/mail-ngrx/sidebars/main/main-sidebar.component.html
#	src/app/main/apps/mail-ngrx/sidebars/main/main-sidebar.component.scss
#	src/app/main/apps/mail-ngrx/store/actions/filters.actions.ts
#	src/app/main/apps/mail-ngrx/store/actions/folders.actions.ts
#	src/app/main/apps/mail-ngrx/store/actions/index.ts
#	src/app/main/apps/mail-ngrx/store/actions/labels.actions.ts
#	src/app/main/apps/mail-ngrx/store/actions/mails.actions.ts
#	src/app/main/apps/mail-ngrx/store/effects/filters.effects.ts
#	src/app/main/apps/mail-ngrx/store/effects/folders.effects.ts
#	src/app/main/apps/mail-ngrx/store/effects/index.ts
#	src/app/main/apps/mail-ngrx/store/effects/labels.effects.ts
#	src/app/main/apps/mail-ngrx/store/effects/mails.effects.ts
#	src/app/main/apps/mail-ngrx/store/guards/index.ts
#	src/app/main/apps/mail-ngrx/store/guards/resolve.guard.ts
#	src/app/main/apps/mail-ngrx/store/index.ts
#	src/app/main/apps/mail-ngrx/store/reducers/filters.reducer.ts
#	src/app/main/apps/mail-ngrx/store/reducers/folders.reducer.ts
#	src/app/main/apps/mail-ngrx/store/reducers/index.ts
#	src/app/main/apps/mail-ngrx/store/reducers/labels.reducer.ts
#	src/app/main/apps/mail-ngrx/store/reducers/mails.reducer.ts
#	src/app/main/apps/mail-ngrx/store/selectors/filters.selectors.ts
#	src/app/main/apps/mail-ngrx/store/selectors/folders.selectors.ts
#	src/app/main/apps/mail-ngrx/store/selectors/index.ts
#	src/app/main/apps/mail-ngrx/store/selectors/labels.selectors.ts
#	src/app/main/apps/mail-ngrx/store/selectors/mails.selectors.ts
#	src/app/main/apps/mail-ngrx/store/store.module.ts
#	src/app/main/apps/mail/dialogs/compose/compose.component.html
#	src/app/main/apps/mail/dialogs/compose/compose.component.scss
#	src/app/main/apps/mail/dialogs/compose/compose.component.ts
#	src/app/main/apps/mail/i18n/en.ts
#	src/app/main/apps/mail/i18n/tr.ts
#	src/app/main/apps/mail/mail-details/mail-details.component.html
#	src/app/main/apps/mail/mail-details/mail-details.component.scss
#	src/app/main/apps/mail/mail-list/mail-list-item/mail-list-item.component.html
#	src/app/main/apps/mail/mail-list/mail-list-item/mail-list-item.component.scss
#	src/app/main/apps/mail/mail-list/mail-list.component.scss
#	src/app/main/apps/mail/mail.component.scss
#	src/app/main/apps/mail/mail.model.ts
#	src/app/main/apps/mail/mail.module.ts
#	src/app/main/apps/mail/mail.service.ts
#	src/app/main/apps/mail/sidebars/main/main-sidebar.component.html
#	src/app/main/apps/mail/sidebars/main/main-sidebar.component.scss
#	src/app/main/apps/scrumboard/board.model.ts
#	src/app/main/apps/scrumboard/board/add-list/add-list.component.html
#	src/app/main/apps/scrumboard/board/add-list/add-list.component.scss
#	src/app/main/apps/scrumboard/board/board.component.html
#	src/app/main/apps/scrumboard/board/board.component.scss
#	src/app/main/apps/scrumboard/board/dialogs/card/card.component.html
#	src/app/main/apps/scrumboard/board/dialogs/card/card.component.scss
#	src/app/main/apps/scrumboard/board/dialogs/card/card.component.ts
#	src/app/main/apps/scrumboard/board/dialogs/card/label-selector/label-selector.component.html
#	src/app/main/apps/scrumboard/board/dialogs/card/label-selector/label-selector.component.scss
#	src/app/main/apps/scrumboard/board/edit-board-name/edit-board-name.component.html
#	src/app/main/apps/scrumboard/board/edit-board-name/edit-board-name.component.scss
#	src/app/main/apps/scrumboard/board/edit-board-name/edit-board-name.component.ts
#	src/app/main/apps/scrumboard/board/list/add-card/add-card.component.html
#	src/app/main/apps/scrumboard/board/list/add-card/add-card.component.scss
#	src/app/main/apps/scrumboard/board/list/card/card.component.html
#	src/app/main/apps/scrumboard/board/list/card/card.component.scss
#	src/app/main/apps/scrumboard/board/list/edit-list-name/edit-list-name.component.html
#	src/app/main/apps/scrumboard/board/list/edit-list-name/edit-list-name.component.scss
#	src/app/main/apps/scrumboard/board/list/list.component.html
#	src/app/main/apps/scrumboard/board/list/list.component.scss
#	src/app/main/apps/scrumboard/board/sidenavs/settings/board-color-selector/board-color-selector.component.html
#	src/app/main/apps/scrumboard/board/sidenavs/settings/board-color-selector/board-color-selector.component.scss
#	src/app/main/apps/scrumboard/board/sidenavs/settings/settings.component.html
#	src/app/main/apps/scrumboard/board/sidenavs/settings/settings.component.scss
#	src/app/main/apps/scrumboard/card.model.ts
#	src/app/main/apps/scrumboard/list.model.ts
#	src/app/main/apps/scrumboard/scrumboard.component.html
#	src/app/main/apps/scrumboard/scrumboard.component.scss
#	src/app/main/apps/scrumboard/scrumboard.service.ts
#	src/app/main/apps/todo/sidebars/main/main-sidebar.component.html
#	src/app/main/apps/todo/sidebars/main/main-sidebar.component.scss
#	src/app/main/apps/todo/todo-details/todo-details.component.html
#	src/app/main/apps/todo/todo-details/todo-details.component.scss
#	src/app/main/apps/todo/todo-list/todo-list-item/todo-list-item.component.html
#	src/app/main/apps/todo/todo-list/todo-list-item/todo-list-item.component.scss
#	src/app/main/apps/todo/todo-list/todo-list.component.scss
#	src/app/main/apps/todo/todo.component.scss
#	src/app/main/apps/todo/todo.model.ts
#	src/app/main/apps/todo/todo.module.ts
#	src/app/main/apps/todo/todo.service.ts
#	src/app/main/content/pages/authentication/mail-confirm/mail-confirm.module.ts
#	src/app/main/content/sample/sample.component.html
#	src/app/main/content/sample/sample.module.ts
#	src/app/main/content/ui/page-layouts/blank/blank.component.html
#	src/app/main/documentation/components-third-party/components-third-party.module.ts
#	src/app/main/documentation/components-third-party/datatable/ngx-datatable.component.html
#	src/app/main/documentation/components-third-party/datatable/ngx-datatable.component.scss
#	src/app/main/documentation/components-third-party/google-maps/google-maps.component.scss
#	src/app/main/documentation/components-third-party/google-maps/google-maps.module.ts
#	src/app/main/documentation/components/cards/cards.component.html
#	src/app/main/documentation/components/cards/cards.component.scss
#	src/app/main/documentation/components/countdown/countdown.component.scss
#	src/app/main/documentation/components/countdown/countdown.component.ts
#	src/app/main/documentation/components/highlight/highlight.component.scss
#	src/app/main/documentation/components/highlight/highlight.component.ts
#	src/app/main/documentation/components/material-color-picker/material-color-picker.component.scss
#	src/app/main/documentation/components/material-color-picker/material-color-picker.component.ts
#	src/app/main/documentation/components/navigation/navigation.component.scss
#	src/app/main/documentation/components/search-bar/search-bar.component.scss
#	src/app/main/documentation/components/search-bar/search-bar.component.ts
#	src/app/main/documentation/components/shortcuts/shortcuts.component.scss
#	src/app/main/documentation/components/shortcuts/shortcuts.component.ts
#	src/app/main/documentation/components/sidebar/sidebar.component.html
#	src/app/main/documentation/components/sidebar/sidebar.component.scss
#	src/app/main/documentation/components/sidebar/sidebar.component.ts
#	src/app/main/documentation/components/widget/widget.component.scss
#	src/app/main/documentation/components/widget/widget.component.ts
#	src/app/main/documentation/directives/fuseIfOnDom/fuse-if-on-dom.component.scss
#	src/app/main/documentation/services/config/config.component.scss
#	src/app/main/documentation/services/config/config.component.ts
#	src/app/main/documentation/services/services.module.ts
#	src/app/main/documentation/services/splash-screen/splash-screen.component.scss
#	src/app/main/documentation/services/splash-screen/splash-screen.component.ts
#	src/app/main/documentation/working-with-fuse/multi-language/multi-language.component.ts
#	src/app/main/pages/authentication/forgot-password-2/forgot-password-2.component.html
#	src/app/main/pages/authentication/forgot-password-2/forgot-password-2.component.scss
#	src/app/main/pages/authentication/forgot-password-2/forgot-password-2.module.ts
#	src/app/main/pages/authentication/forgot-password/forgot-password.component.html
#	src/app/main/pages/authentication/forgot-password/forgot-password.component.scss
#	src/app/main/pages/authentication/forgot-password/forgot-password.module.ts
#	src/app/main/pages/authentication/lock/lock.component.html
#	src/app/main/pages/authentication/lock/lock.component.scss
#	src/app/main/pages/authentication/lock/lock.module.ts
#	src/app/main/pages/authentication/login-2/login-2.component.html
#	src/app/main/pages/authentication/login-2/login-2.component.scss
#	src/app/main/pages/authentication/login-2/login-2.module.ts
#	src/app/main/pages/authentication/login/login.component.html
#	src/app/main/pages/authentication/login/login.component.scss
#	src/app/main/pages/authentication/login/login.module.ts
#	src/app/main/pages/authentication/mail-confirm/mail-confirm.component.html
#	src/app/main/pages/authentication/mail-confirm/mail-confirm.component.scss
#	src/app/main/pages/authentication/mail-confirm/mail-confirm.module.ts
#	src/app/main/pages/authentication/register-2/register-2.component.html
#	src/app/main/pages/authentication/register-2/register-2.component.scss
#	src/app/main/pages/authentication/register-2/register-2.module.ts
#	src/app/main/pages/authentication/register/register.component.html
#	src/app/main/pages/authentication/register/register.component.scss
#	src/app/main/pages/authentication/register/register.module.ts
#	src/app/main/pages/authentication/reset-password-2/reset-password-2.component.html
#	src/app/main/pages/authentication/reset-password-2/reset-password-2.component.scss
#	src/app/main/pages/authentication/reset-password-2/reset-password-2.module.ts
#	src/app/main/pages/authentication/reset-password/reset-password.component.html
#	src/app/main/pages/authentication/reset-password/reset-password.component.scss
#	src/app/main/pages/authentication/reset-password/reset-password.module.ts
#	src/app/main/pages/coming-soon/coming-soon.component.html
#	src/app/main/pages/coming-soon/coming-soon.component.scss
#	src/app/main/pages/coming-soon/coming-soon.module.ts
#	src/app/main/pages/errors/404/error-404.component.html
#	src/app/main/pages/errors/404/error-404.component.scss
#	src/app/main/pages/errors/404/error-404.module.ts
#	src/app/main/pages/errors/500/error-500.component.html
#	src/app/main/pages/errors/500/error-500.component.scss
#	src/app/main/pages/errors/500/error-500.module.ts
#	src/app/main/pages/faq/faq.component.html
#	src/app/main/pages/faq/faq.component.scss
#	src/app/main/pages/faq/faq.module.ts
#	src/app/main/pages/faq/faq.service.ts
#	src/app/main/pages/invoices/compact/compact.component.html
#	src/app/main/pages/invoices/compact/compact.component.scss
#	src/app/main/pages/invoices/compact/compact.module.ts
#	src/app/main/pages/invoices/invoice.service.ts
#	src/app/main/pages/invoices/modern/modern.component.html
#	src/app/main/pages/invoices/modern/modern.component.scss
#	src/app/main/pages/invoices/modern/modern.module.ts
#	src/app/main/pages/knowledge-base/dialogs/article/article.component.html
#	src/app/main/pages/knowledge-base/dialogs/article/article.component.scss
#	src/app/main/pages/knowledge-base/knowledge-base.component.html
#	src/app/main/pages/knowledge-base/knowledge-base.component.scss
#	src/app/main/pages/knowledge-base/knowledge-base.module.ts
#	src/app/main/pages/knowledge-base/knowledge-base.service.ts
#	src/app/main/pages/maintenance/maintenance.component.html
#	src/app/main/pages/maintenance/maintenance.component.scss
#	src/app/main/pages/maintenance/maintenence.module.ts
#	src/app/main/pages/pricing/pricing.module.ts
#	src/app/main/pages/pricing/style-1/style-1.component.html
#	src/app/main/pages/pricing/style-1/style-1.component.scss
#	src/app/main/pages/pricing/style-1/style-1.component.ts
#	src/app/main/pages/pricing/style-2/style-2.component.html
#	src/app/main/pages/pricing/style-2/style-2.component.scss
#	src/app/main/pages/pricing/style-2/style-2.component.ts
#	src/app/main/pages/pricing/style-3/style-3.component.html
#	src/app/main/pages/pricing/style-3/style-3.component.scss
#	src/app/main/pages/pricing/style-3/style-3.component.ts
#	src/app/main/pages/profile/profile.component.html
#	src/app/main/pages/profile/profile.component.scss
#	src/app/main/pages/profile/profile.component.ts
#	src/app/main/pages/profile/profile.module.ts
#	src/app/main/pages/profile/profile.service.ts
#	src/app/main/pages/profile/tabs/about/about.component.html
#	src/app/main/pages/profile/tabs/about/about.component.scss
#	src/app/main/pages/profile/tabs/photos-videos/photos-videos.component.html
#	src/app/main/pages/profile/tabs/photos-videos/photos-videos.component.scss
#	src/app/main/pages/profile/tabs/timeline/timeline.component.html
#	src/app/main/pages/profile/tabs/timeline/timeline.component.scss
#	src/app/main/pages/search/search.component.html
#	src/app/main/pages/search/search.component.scss
#	src/app/main/pages/search/search.component.ts
#	src/app/main/pages/search/search.module.ts
#	src/app/main/pages/search/search.service.ts
#	src/app/main/pages/search/tabs/classic/classic.component.html
#	src/app/main/pages/search/tabs/classic/classic.component.scss
#	src/app/main/pages/search/tabs/table/table.component.html
#	src/app/main/quick-panel/quick-panel.component.ts
#	src/app/main/ui/colors/colors.component.html
#	src/app/main/ui/colors/colors.component.scss
#	src/app/main/ui/colors/colors.module.ts
#	src/app/main/ui/forms/forms.component.html
#	src/app/main/ui/forms/forms.component.scss
#	src/app/main/ui/forms/forms.component.ts
#	src/app/main/ui/forms/forms.module.ts
#	src/app/main/ui/helper-classes/helper-classes.component.html
#	src/app/main/ui/helper-classes/helper-classes.component.scss
#	src/app/main/ui/helper-classes/helper-classes.component.ts
#	src/app/main/ui/helper-classes/helper-classes.module.ts
#	src/app/main/ui/helper-classes/tabs/padding-margin/padding-margin.component.html
#	src/app/main/ui/helper-classes/tabs/padding-margin/padding-margin.component.scss
#	src/app/main/ui/helper-classes/tabs/padding-margin/padding-margin.component.ts
#	src/app/main/ui/helper-classes/tabs/width-height/width-height.component.html
#	src/app/main/ui/helper-classes/tabs/width-height/width-height.component.scss
#	src/app/main/ui/helper-classes/tabs/width-height/width-height.component.ts
#	src/app/main/ui/icons/icons.component.html
#	src/app/main/ui/icons/icons.component.scss
#	src/app/main/ui/icons/icons.module.ts
#	src/app/main/ui/page-layouts/blank/blank.component.html
#	src/app/main/ui/page-layouts/blank/blank.component.ts
#	src/app/main/ui/page-layouts/carded/full-width-1/full-width-1.component.html
#	src/app/main/ui/page-layouts/carded/full-width-2/full-width-2.component.html
#	src/app/main/ui/page-layouts/simple/full-width-1/full-width-1.component.html
#	src/app/main/ui/page-layouts/simple/full-width-tabbed-1/full-width-tabbed-1.component.html
#	src/app/main/ui/typography/tabs/blockquotes-lists/blockquotes-lists.component.html
#	src/app/main/ui/typography/tabs/blockquotes-lists/blockquotes-lists.component.scss
#	src/app/main/ui/typography/tabs/blockquotes-lists/blockquotes-lists.component.ts
#	src/app/main/ui/typography/tabs/headings/headings.component.html
#	src/app/main/ui/typography/tabs/headings/headings.component.scss
#	src/app/main/ui/typography/tabs/headings/headings.component.ts
#	src/app/main/ui/typography/tabs/helpers/helpers.component.html
#	src/app/main/ui/typography/tabs/helpers/helpers.component.scss
#	src/app/main/ui/typography/tabs/helpers/helpers.component.ts
#	src/app/main/ui/typography/tabs/inline-text-elements/inline-text-elements.component.html
#	src/app/main/ui/typography/tabs/inline-text-elements/inline-text-elements.component.scss
#	src/app/main/ui/typography/tabs/inline-text-elements/inline-text-elements.component.ts
#	src/app/main/ui/typography/typography.component.html
#	src/app/main/ui/typography/typography.component.scss
#	src/app/main/ui/typography/typography.component.ts
#	src/app/navigation/navigation.ts
#	src/app/store/actions/router.action.ts
#	src/app/store/effects/router.effect.ts
#	src/app/store/reducers/index.ts
#	src/app/store/store.module.ts
2018-06-15 18:36:24 +03:00
Sercan Yemen
75b7e4e270 Merge branch 'dev/new-fuse' 2018-06-15 17:56:36 +03:00
Sercan Yemen
9efab7ed20 Fixed private methods and injections 2018-06-15 17:46:41 +03:00
Sercan Yemen
65523c3c95 Fixed private methods and injections 2018-06-15 17:45:27 +03:00
Sercan Yemen
22b3a3c799 Reverted the ngx-dnd version to 4.0.0 2018-06-15 17:33:16 +03:00
Sercan Yemen
2b4bd45ad6 Updated Angular and couple other packages 2018-06-15 17:16:41 +03:00
Sercan Yemen
1e47c79f15 Add !important tag to the sibling's margin on sidebar folded 2018-06-15 17:01:02 +03:00
Sercan Yemen
58ab766edf Removed router animations since they are causing problems on Edge and also they make Fuse feel slower 2018-06-15 16:37:19 +03:00
Sercan Yemen
8db1206c91 Removed router animations since they are causing problems on Edge and also they make Fuse feel slower 2018-06-15 16:36:51 +03:00
Sercan Yemen
562ae61098 Added the changelog 2018-06-15 16:28:18 +03:00
Sercan Yemen
e43ae86a58 IE11 and Edge fixes on page layouts and theme layouts 2018-06-15 16:28:02 +03:00
Sercan Yemen
5459579d04 Added docs styles
Improved the documentation and moved it into the Demo app
2018-06-15 13:23:12 +03:00
Sercan Yemen
95759be710 Use the navigation service in the fuse-navigation for easier navigation swap 2018-06-14 18:00:22 +03:00
Sercan Yemen
edb4683dcb Use the navigation service in the fuse-navigation for easier navigation swap 2018-06-14 17:29:30 +03:00
Sercan Yemen
58dd3b93c3 message-box alert => message-box error 2018-06-14 13:41:18 +03:00
Sercan Yemen
91e8d88488 Import the "bash" language color from prism
Added "message-boxes" styles to the typography
Increased the home icon size in doc pages
Changed doc component class names
2018-06-14 12:25:03 +03:00
Sercan Yemen
ddcf1d5483 Added changelog and changelog style to the typography
Small tweaks on helper classes and typography pages
(Fuse Highlight) Don't wrap the code and show scrollbars instead
2018-06-12 17:06:38 +03:00
Sercan Yemen
0b5727ff15 Small tweaks 2018-06-12 14:52:13 +03:00
Sercan Yemen
20f80e3fe4 Page layout tweaks
Removed body color class
2018-06-12 12:09:00 +03:00
Sercan Yemen
5bf9fd177b Updated footer links 2018-06-12 10:51:16 +03:00
Sercan Yemen
507fe97e84 Changed layout names to make them easier to understand
New navigation structure for documentation and Angular Material elements
2018-06-12 10:44:00 +03:00
Sercan Yemen
a6a7442607 Search icon color 2018-06-12 10:28:53 +03:00
Sercan Yemen
43189728e5 Navbar toggle button should be right aligned if right navbar selected
Navbar classes actually moved to the navbar from fuse-sidebar
Search icon color
2018-06-12 10:28:08 +03:00
Sercan Yemen
9c88524185 Moved Angular Material Elements examples
Prepared the navigation for the documentation update
2018-06-12 10:27:17 +03:00
Sercan Yemen
52cfeec2e7 (Layout) Apps and pages moved to the new layout system 2018-06-11 19:15:12 +03:00
Sercan Yemen
ffb134f1ec (Perfect Scrollbar) Improved the fusePerfectScrollbar directive 2018-06-11 19:03:39 +03:00
Sercan Yemen
8520ca77be Icon colors on navigations 2018-06-11 18:18:01 +03:00
Sercan Yemen
a174c00072 (Apps) Moved the apps to the new page layouts and replaced MatSidenav with FuseSidebar on majority of them 2018-06-11 16:48:53 +03:00
Sercan Yemen
42ab15d9e1 (Apps) Moved the apps to the new page layouts and replaced MatSidenav with FuseSidebar on majority of them 2018-06-11 16:45:07 +03:00
Sercan Yemen
a87e68251e (Apps) Moved the apps to the new page layouts and replaced MatSidenav with FuseSidebar on majority of them 2018-06-11 13:26:03 +03:00
Sercan Yemen
436bd0aa91 (Page layouts) Small tweaks 2018-06-11 13:25:26 +03:00
Sercan Yemen
d5f1fcfefa (Sidebar) Mark the change detector for changes on every action so OnPush components can use the Sidebar 2018-06-11 12:10:04 +03:00
Sercan Yemen
234dec3d6a (Page layouts) Small tweaks
(Config) Disable the router animation by default
2018-06-11 12:02:54 +03:00
Sercan Yemen
928be05725 (Page Layouts) Small tweaks and fixes for native scrollbars 2018-06-11 09:27:56 +03:00
Sercan Yemen
bcf5a9e6cb (Navigation) Moved "Angular Material Elements" outside of the "Components" menu item 2018-06-10 20:27:29 +03:00
Sercan Yemen
e79bfb7bce (Page Layouts) Renamed simple "Tabbed" layout to "Full Width Tabbed" + small style tweak 2018-06-10 20:25:27 +03:00
Sercan Yemen
86815b7737 (Page Layouts) Small tweaks 2018-06-10 20:16:01 +03:00
Sercan Yemen
73a81699ec (Page Layouts) Removed mat-sidenav from page layouts, now all Fuse page layouts uses Fuse Sidebar 2018-06-10 20:00:34 +03:00
Sercan Yemen
c2970e34ba Content component small tweaks 2018-06-10 19:46:33 +03:00
Sercan Yemen
7d6a92fada (FusePerfectScrollbar) Added options for configuring the scrollbar
Updated layouts and navbar to make use of the scrollbar options
2018-06-10 19:46:10 +03:00
Sercan Yemen
acbed8e305 (Sidebar) Don't enable the animations right away to prevent initial animations 2018-06-10 19:32:01 +03:00
Sercan Yemen
b46f81b6ac Updated packages
Increased the Fuse version
2018-06-10 19:31:14 +03:00
Sercan Yemen
a284063d22 Added an extra Material theme as an example to the "styles.scss" file
Fixed bunch of color related stuff in order to apply the selected theme color correctly
2018-06-09 21:18:30 +03:00
Sercan Yemen
1c8cc35693 Fixed router animations after the layout changes
Don't reset the router animation on navigation change
2018-06-09 14:58:45 +03:00
Sercan Yemen
6cc7d03430 Removed console.log 2018-06-09 14:44:50 +03:00
Sercan Yemen
f8f97f8ad4 Remove 'inner-scroll' class on small screen devices to give more room to the scroll 2018-06-09 14:43:42 +03:00
Sercan Yemen
bd79830cb4 Fixed: Toolbar background color issues 2018-06-09 14:26:00 +03:00
Sercan Yemen
5ed04c3925 Collapsable arrow class name fix 2018-06-07 21:20:09 +03:00
Sercan Yemen
90fe94a417 Added horizontal layout to the new layout system
Added boxed layout width
2018-06-07 21:12:12 +03:00
Sercan Yemen
a39021188e Renamed 'collapse' to 'collapsable' in navigation types
Renamed navigation component files
Small fix in shortcuts ('nav-item' to 'item')
2018-06-07 20:49:29 +03:00
Sercan Yemen
ba35ebae94 Fix: Images in Angular Material cards 2018-06-07 19:11:21 +03:00
Sercan Yemen
97f864bb76 Small fix in navigation docs 2018-06-07 19:11:06 +03:00
Sercan Yemen
ea15a8b832 Re-added the accidentally deleted Angular Material Examples module 2018-06-07 19:10:44 +03:00
Sercan Yemen
5c1f2ad1e3 New layout system & new layouts (wip) 2018-06-06 21:20:04 +03:00
Sercan Yemen
420d8d1a1b Navigation icon colors 2018-06-06 21:19:43 +03:00
Sercan Yemen
f693298f3a Navigation interface 2018-05-30 19:48:59 +03:00
Sercan Yemen
a29c4b01ad Update the readme 2018-05-30 12:31:58 +03:00
Sercan Yemen
1b94d8d14e Config types 2018-05-30 12:23:31 +03:00
Sercan Yemen
232b4de752 Moved ThemeOptions to the Fuse Sidebar
Replaced align with position on Fuse Sidebar components
2018-05-30 12:23:09 +03:00
Sercan Yemen
b918fa4122 (Sidebar) Changed align to position since align is a native HTML attribute
Added invisibleOverlay option
2018-05-30 12:22:19 +03:00
Sercan Yemen
83c395b866 (Sidebar) Make the sidebar invisible when it's not open
Made the backdrop methods private
2018-05-30 11:49:30 +03:00
Sercan Yemen
387077882b Codebase improvements 2018-05-29 15:07:13 +03:00
Sercan Yemen
70d895c6ed New layout system (wip) 2018-05-29 14:58:48 +03:00
Sercan Yemen
0ac4e6c220 (Academy app) Small shadow adjustment 2018-05-28 17:41:33 +03:00
Sercan Yemen
850e43c653 (Navigation) Improved navigation service 2018-05-28 16:04:27 +03:00
Sercan Yemen
1e6bd8139c Removed bg color from body 2018-05-28 12:32:29 +03:00
Sercan Yemen
c6969cf9df (Navigation) Improved navigation service for easier navigation management 2018-05-28 12:31:54 +03:00
Sercan Yemen
745c51878d Small color adjustments 2018-05-27 16:58:26 +03:00
Sercan Yemen
9635165316 (Config Service) Added ability to access to the default config and reset to it 2018-05-27 16:57:54 +03:00
Sercan Yemen
02f305be1f Improved the FusePerfectScrollbar directive, not directives can be controlled individually by adding a boolean value to them 2018-05-27 16:56:50 +03:00
Sercan Yemen
27197a55dc Improving the codebase 2018-05-27 16:55:46 +03:00
Sercan Yemen
5d000a849d Updated couple packages 2018-05-27 16:54:20 +03:00
Sercan Yemen
07341c5ffa Improving the codebase (99% completed)
Updated Angular, Angular Material and couple other packages
2018-05-23 12:35:08 +03:00
Sercan Yemen
742da904da Improving the codebase (wip) 2018-05-21 15:42:34 +03:00
Sercan Yemen
880529ad62 Temporary solution to the ngx-translate issue while we are working on a better solution 2018-05-20 16:03:11 +03:00
Sercan Yemen
126ba35d3d Updated Angular and various other packages 2018-05-20 10:19:15 +03:00
Sercan Yemen
0039f44936 Improving the codebase (wip) 2018-05-20 10:12:31 +03:00
Sercan Yemen
ced0853af8 (mail-ngrx) Use absolute paths on imports 2018-05-18 17:40:57 +03:00
Sercan Yemen
3d483b5a4b Started building the new layout system along with the new layouts 2018-05-18 17:18:33 +03:00
Sercan Yemen
96813406a2 Fixed: Horizontal nav titles are collapsing on IE11 2018-05-16 14:16:40 +03:00
Sercan Yemen
7fa892d7cc Updated Angular and Angular Material
Increased the Fuse version
Small update on karma.conf.js
2018-05-15 14:09:01 +03:00
Sercan Yemen
832a08208a (Toolbar) Set the selected the language from the translation service 2018-05-15 13:52:01 +03:00
Sercan Yemen
3bd2ad9519 Merge branch 'master' into skeleton 2018-05-09 18:18:30 +03:00
Sercan Yemen
eb231c5ca8 angular.json for skeleton 2018-05-06 14:47:26 +03:00
Sercan Yemen
954d61b73a Merge branch 'master' into skeleton 2018-05-06 14:47:07 +03:00
Sercan Yemen
46c37042a2 Merge branch 'master' into skeleton 2018-04-04 14:42:49 +03:00
Sercan Yemen
47ee65a980 Merge branch 'master' into skeleton 2018-03-31 13:22:27 +03:00
Sercan Yemen
5f974c4ed2 Merge branch 'master' into skeleton 2018-03-11 18:27:10 +03:00
Sercan Yemen
605f4d9463 Merge branch 'master' into skeleton 2018-03-10 14:35:24 +03:00
Sercan Yemen
4be77a19ed Removed unnecessary lines from angular-cli.json file 2018-03-09 20:17:38 +03:00
Sercan Yemen
8374c7d059 Merge branch 'master' into skeleton 2018-03-09 20:14:36 +03:00
Sercan Yemen
da615585d0 Merge branch 'master' into skeleton 2018-03-09 20:03:43 +03:00
Sercan Yemen
7c50487164 Removed unnecessary Angular Material example files, fixes AoT builds 2018-03-09 06:30:59 +03:00
Sercan Yemen
97c7f136bf Merge branch 'master' into skeleton 2018-03-08 12:44:21 +03:00
Sercan Yemen
1cddda02b1 Merge branch 'master' into skeleton 2018-03-08 12:42:14 +03:00
Sercan Yemen
c178eeedaa Merge branch 'master' into skeleton 2018-03-08 12:38:51 +03:00
Sercan Yemen
43b22e609e Merge branch 'master' into skeleton 2018-02-08 11:06:42 +03:00
Sercan Yemen
a914ad6dc1 Merge branch 'master' into skeleton 2018-02-05 17:17:24 +03:00
Sercan Yemen
b2e840cb60 Merge branch 'master' into skeleton 2018-01-23 17:03:44 +03:00
Sercan Yemen
d7c67ca5a8 Updated package-lock.json file 2018-01-18 13:34:27 +03:00
Sercan Yemen
cf7ab3861d Merge branch 'master' into skeleton 2018-01-18 13:21:58 +03:00
Sercan Yemen
ab4ed81cfc Merge branch 'master' into skeleton 2018-01-18 13:19:38 +03:00
Sercan Yemen
2022b7307e Merge branch 'master' into skeleton 2018-01-11 13:37:32 +03:00
Sercan Yemen
46de82a7fa Merge branch 'master' into skeleton 2018-01-11 13:37:03 +03:00
Sercan Yemen
bc2b2c75fa Merge branch 'master' into skeleton 2018-01-08 16:41:04 +03:00
Sercan Yemen
02653cd0f4 Merge branch 'master' into skeleton 2018-01-08 16:40:33 +03:00
Sercan Yemen
ff14879a94 Merge branch 'master' into skeleton 2018-01-08 16:11:29 +03:00
Sercan Yemen
49c49c46d1 Merge branch 'master' into skeleton 2018-01-08 16:09:12 +03:00
Sercan Yemen
915e9203ef Merge branch 'master' into skeleton 2018-01-08 16:08:56 +03:00
Sercan Yemen
33d295f42c Merge branch 'master' into skeleton 2018-01-02 12:37:12 +03:00
Sercan Yemen
91e277ce3f Merge branch 'master' into skeleton 2018-01-02 12:32:01 +03:00
Sercan Yemen
b7a3d35eb8 Updated package-lock.json 2017-12-28 10:52:40 +03:00
Sercan Yemen
f29f11232f Merge branch 'master' into skeleton 2017-12-28 10:51:09 +03:00
Sercan Yemen
16ffb09350 Merge branch 'master' into skeleton 2017-12-26 10:49:15 +03:00
Sercan Yemen
643a129a46 Merge branch 'master' into skeleton 2017-12-21 10:06:16 +03:00
Sercan Yemen
de16f4f866 Merge branch 'master' into skeleton 2017-12-18 12:25:56 +03:00
Sercan Yemen
415d7cebfa Skeleton updates 2017-12-14 16:20:52 +03:00
Sercan Yemen
f7d1995f63 Merge branch 'master' into skeleton 2017-12-14 16:06:32 +03:00
Sercan Yemen
3741abc063 Skeleton package updates 2017-12-14 16:03:34 +03:00
Sercan Yemen
54ccdd7de2 Merge branch 'master' into skeleton 2017-12-14 16:01:09 +03:00
Sercan Yemen
8b2e6b95b1 Merge branch 'master' into skeleton 2017-11-30 15:56:00 +03:00
Sercan Yemen
e86cea1e73 Merge branch 'master' into skeleton 2017-11-30 10:38:03 +03:00
Sercan Yemen
b81638690e Merge branch 'master' into skeleton 2017-11-30 10:23:19 +03:00
Sercan Yemen
20ac3abb25 Merge branch 'master' into skeleton 2017-11-27 17:21:38 +03:00
Sercan Yemen
f634cb06a7 Merge branch 'master' into skeleton 2017-11-27 14:42:23 +03:00
Sercan Yemen
7d67a481ff Merge branch 'master' into skeleton 2017-11-27 14:35:37 +03:00
Sercan Yemen
4659da7390 Added missing variables that prevents skeleton from building as it is 2017-11-13 12:30:11 +03:00
Sercan Yemen
2a5d15694c Merge branch 'master' into skeleton 2017-11-13 11:09:27 +03:00
Sercan Yemen
8e6024c3ee Fixes #44 : Removed angular material elements assets 2017-11-08 15:11:20 +03:00
Sercan Yemen
f4c47daadc Merge branch 'master' into skeleton 2017-11-08 15:10:51 +03:00
Sercan Yemen
381bc6c0fe Merge branch 'master' into skeleton 2017-11-04 16:50:03 +03:00
Sercan Yemen
b5a139f81d Merge branch 'master' into skeleton 2017-11-04 16:32:29 +03:00
Sercan Yemen
914477da41 Merge branch 'master' into skeleton 2017-11-04 16:25:09 +03:00
Sercan Yemen
410802808e Merge branch 'master' into skeleton 2017-11-04 16:20:46 +03:00
Sercan Yemen
56dbc58d5e Merge branch 'master' into skeleton
+ Added translation example to the sample page
2017-10-27 12:01:09 +03:00
Sercan Yemen
d7c6b2d617 Merge branch 'master' into skeleton 2017-10-27 11:49:31 +03:00
Sercan Yemen
80627bdde9 removed fake-db thingy 2017-10-16 10:50:57 +03:00
Sercan Yemen
6595975f2b Merge branch 'master' into skeleton 2017-10-16 10:45:27 +03:00
Sercan Yemen
dcb8032758 Merge branch 'master' into skeleton 2017-10-16 10:10:06 +03:00
Sercan Yemen
fb214da5fe Merge branch 'master' into skeleton 2017-10-14 18:53:08 +03:00
Sercan Yemen
e20687034f Merge branch 'master' into skeleton 2017-10-14 18:53:00 +03:00
Sercan Yemen
f9bda99deb Merge branch 'master' into skeleton 2017-10-02 10:15:00 +03:00
Sercan Yemen
1d81e37a0f removed markdown module imports 2017-09-28 13:00:44 +03:00
Sercan Yemen
83f0ed5ec1 Merge branch 'master' into skeleton 2017-09-28 12:52:59 +03:00
Sercan Yemen
e486413872 remove unnecessary stuff from skeleton 2017-09-28 12:51:15 +03:00
Sercan Yemen
576e167ef1 Merge branch 'master' into skeleton 2017-09-28 12:49:18 +03:00
Sercan Yemen
cf9e9fc209 Merge branch 'master' into skeleton 2017-09-22 16:45:47 +03:00
Sercan Yemen
ff0f2933d9 Merge branch 'master' into skeleton 2017-09-22 16:45:23 +03:00
Sercan Yemen
62467c8ddf Merge branch 'master' into skeleton 2017-09-12 16:06:21 +03:00
Sercan Yemen
024ab15b25 Merge branch 'master' into skeleton 2017-09-11 16:34:07 +03:00
Sercan Yemen
915ad52863 Merge branch 'master' into skeleton 2017-09-11 16:21:05 +03:00
Sercan Yemen
97bfaa9979 Merge branch 'master' into skeleton 2017-09-11 13:06:42 +03:00
Sercan Yemen
6ae3e154c3 Merge branch 'master' into skeleton 2017-09-11 12:59:36 +03:00
Sercan Yemen
49b6ff7292 navigation model for horizontal nav 2017-09-11 12:58:24 +03:00
Sercan Yemen
903688ab43 Merge branch 'master' into skeleton 2017-09-11 12:57:59 +03:00
Sercan Yemen
19f822cbab removed unnecessary md2 from skeleton 2017-09-01 09:05:56 +03:00
Sercan Yemen
1d21a14d0e Merge branch 'master' into skeleton 2017-08-30 20:52:23 +03:00
Sercan Yemen
4bf2ba73ad merge 'master' into skeleton 2017-08-30 20:08:12 +03:00
Sercan Yemen
6a3972fff8 Merge branch 'master' into skeleton 2017-08-30 14:43:10 +03:00
Sercan Yemen
dca16238eb Merge branch 'master' into skeleton 2017-08-30 14:41:30 +03:00
Sercan Yemen
2b91119d00 Merge branch 'master' into skeleton 2017-08-30 11:50:56 +03:00
Sercan Yemen
ff4899e8d2 Merge branch 'master' into skeleton
# Conflicts:
#	src/app/app.module.ts
#	src/app/main/content/apps/calendar/calendar.component.html
#	src/app/main/content/apps/contacts/contact-list/contact-list.component.html
#	src/app/main/content/apps/contacts/contacts.component.html
#	src/app/main/content/apps/contacts/contacts.component.scss
#	src/app/main/content/apps/contacts/contacts.module.ts
#	src/app/main/content/apps/contacts/contacts.service.ts
#	src/app/main/content/apps/contacts/selected-bar/selected-bar.component.html
#	src/app/main/content/apps/mail/sidenavs/main/main-sidenav.component.html
#	src/app/navigation.model.ts
2017-08-24 11:18:24 +03:00
Sercan Yemen
e818c53f1d navbar fixes 2017-08-22 16:05:32 +03:00
Sercan Yemen
ca96fffadf removed fuse fake db 2017-08-22 15:58:35 +03:00
Sercan Yemen
d7003711ee skeleton branch 2017-08-22 15:55:48 +03:00
1154 changed files with 8782 additions and 63869 deletions

View File

@@ -1,13 +1,6 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
charset=utf-8
end_of_line=lf
insert_final_newline=false
indent_style=space
indent_size=4

View File

@@ -1,6 +1,6 @@
# Fuse2
# Fuse - Angular
Material Design Admin Template with Angular 6+ and Angular Material 2
Material Design Admin Template with Angular 6+ and Angular Material
## The Community

View File

@@ -24,8 +24,7 @@
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets",
"src/app/main/content/components/angular-material"
"src/assets"
],
"styles": [
"src/styles.scss"
@@ -49,6 +48,18 @@
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
},
"ec": {
"sourceMap": true,
"extractCss": true
},
"hmr": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.hmr.ts"
}
]
}
}
},
@@ -63,12 +74,10 @@
},
"hmr": {
"hmr": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.hmr.ts"
}
]
"browserTarget": "fuse:build:hmr"
},
"ec": {
"browserTarget": "fuse:build:ec"
}
}
},
@@ -103,9 +112,7 @@
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**",
"**/src/app/fuse-fake-db/**/*",
"**/src/assets/angular-material-examples/**/*"
"**/node_modules/**"
]
}
}
@@ -127,9 +134,7 @@
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**",
"**/src/app/fuse-fake-db/**/*",
"**/src/assets/angular-material-examples/**/*"
"**/node_modules/**"
]
}
}

2389
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
{
"name": "fuse",
"version": "6.0.1",
"version": "6.2.3",
"license": "https://themeforest.net/licenses/terms/regular",
"scripts": {
"ng": "ng",
"start": "ng serve --open",
"start-hmr": "ng serve --configuration hmr -sm=false",
"start-hmr-sourcemaps": "ng serve --hmr -e=hmr",
"start-hmr": "ng serve --configuration hmr --source-map=false --hmr-warning=false",
"start-hmr-sourcemaps": "ng serve --configuration hmr --source-map=true --hmr-warning=false",
"build": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --dev",
"build-stats": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --dev --stats-json",
"build-prod": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --prod",
@@ -18,71 +18,71 @@
},
"private": true,
"dependencies": {
"@agm/core": "1.0.0-beta.2",
"@angular/animations": "6.0.0",
"@angular/cdk": "6.0.1",
"@angular/common": "6.0.0",
"@angular/compiler": "6.0.0",
"@angular/core": "6.0.0",
"@angular/flex-layout": "6.0.0-beta.15",
"@angular/forms": "6.0.0",
"@angular/http": "6.0.0",
"@angular/material": "6.0.1",
"@angular/material-moment-adapter": "6.0.1",
"@angular/platform-browser": "6.0.0",
"@angular/platform-browser-dynamic": "6.0.0",
"@angular/router": "6.0.0",
"@ngrx/effects": "6.0.0-beta.1",
"@ngrx/router-store": "6.0.0-beta.1",
"@ngrx/store": "6.0.0-beta.1",
"@ngrx/store-devtools": "6.0.0-beta.1",
"@ngx-translate/core": "10.0.1",
"@swimlane/ngx-charts": "8.0.0",
"@swimlane/ngx-datatable": "12.0.0",
"@agm/core": "1.0.0-beta.3",
"@angular/animations": "6.0.7",
"@angular/cdk": "6.3.3",
"@angular/common": "6.0.7",
"@angular/compiler": "6.0.7",
"@angular/core": "6.0.7",
"@angular/flex-layout": "6.0.0-beta.16",
"@angular/forms": "6.0.7",
"@angular/http": "6.0.7",
"@angular/material": "6.3.3",
"@angular/material-moment-adapter": "6.3.3",
"@angular/platform-browser": "6.0.7",
"@angular/platform-browser-dynamic": "6.0.7",
"@angular/router": "6.0.7",
"@ngrx/effects": "6.0.1",
"@ngrx/router-store": "6.0.1",
"@ngrx/store": "6.0.1",
"@ngrx/store-devtools": "6.0.1",
"@ngx-translate/core": "10.0.2",
"@swimlane/ngx-charts": "8.1.0",
"@swimlane/ngx-datatable": "13.0.1",
"@swimlane/ngx-dnd": "4.0.0",
"@types/prismjs": "1.9.0",
"angular-calendar": "0.24.0",
"angular-calendar": "0.25.2",
"angular-in-memory-web-api": "0.6.0",
"chart.js": "2.7.2",
"classlist.js": "1.1.20150312",
"core-js": "2.5.6",
"d3": "5.2.0",
"core-js": "2.5.7",
"d3": "5.5.0",
"hammerjs": "2.0.8",
"lodash": "4.17.10",
"moment": "2.22.1",
"moment": "2.22.2",
"ng2-charts": "1.6.0",
"ngrx-store-freeze": "0.2.2",
"ngx-color-picker": "6.0.0",
"ngrx-store-freeze": "0.2.4",
"ngx-color-picker": "6.4.0",
"ngx-cookie-service": "1.0.10",
"perfect-scrollbar": "1.3.0",
"prismjs": "1.14.0",
"rxjs": "6.1.0",
"rxjs-compat": "6.1.0",
"perfect-scrollbar": "1.4.0",
"prismjs": "1.15.0",
"rxjs": "6.2.1",
"rxjs-compat": "6.2.1",
"web-animations-js": "2.3.1",
"zone.js": "0.8.26"
},
"devDependencies": {
"@angular/cli": "6.0.0",
"@angular/compiler-cli": "6.0.0",
"@angular/language-service": "6.0.0",
"@angular-devkit/build-angular": "0.6.0",
"@angular/cli": "6.0.8",
"@angular/compiler-cli": "6.0.7",
"@angular/language-service": "6.0.7",
"@angular-devkit/build-angular": "0.6.8",
"@angularclass/hmr": "2.1.3",
"@types/jasmine": "2.8.7",
"@types/jasmine": "2.8.8",
"@types/jasminewd2": "2.0.3",
"@types/lodash": "4.14.108",
"@types/lodash": "4.14.110",
"@types/node": "8.9.5",
"codelyzer": "4.2.1",
"jasmine-core": "2.99.1",
"jasmine-spec-reporter": "4.2.1",
"karma": "1.7.1",
"karma-chrome-launcher": "2.2.0",
"karma-coverage-istanbul-reporter": "1.4.2",
"karma-coverage-istanbul-reporter": "2.0.1",
"karma-jasmine": "1.1.2",
"karma-jasmine-html-reporter": "0.2.2",
"protractor": "5.3.1",
"protractor": "5.3.2",
"ts-node": "5.0.1",
"tslint": "5.9.1",
"typescript": "2.7.2",
"webpack-bundle-analyzer": "2.11.1"
"webpack-bundle-analyzer": "2.13.1"
}
}

View File

@@ -187,10 +187,14 @@ export const fuseAnimations = [
transition('* => void', animate('300ms ease-in'))
]),
// -----------------------------------------------------------------------------------------------------
// @ Router animations
// -----------------------------------------------------------------------------------------------------
trigger('routerTransitionLeft', [
transition('* => *', [
query('fuse-content > :enter, fuse-content > :leave', [
query('content > :enter, content > :leave', [
style({
position: 'absolute',
top : 0,
@@ -199,7 +203,7 @@ export const fuseAnimations = [
right : 0
})
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({
transform: 'translateX(100%)',
opacity : 0
@@ -207,7 +211,7 @@ export const fuseAnimations = [
], {optional: true}),
sequence([
group([
query('fuse-content > :leave', [
query('content > :leave', [
style({
transform: 'translateX(0)',
opacity : 1
@@ -218,7 +222,7 @@ export const fuseAnimations = [
opacity : 0
}))
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({transform: 'translateX(100%)'}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
@@ -227,8 +231,8 @@ export const fuseAnimations = [
}))
], {optional: true})
]),
query('fuse-content > :leave', animateChild(), {optional: true}),
query('fuse-content > :enter', animateChild(), {optional: true})
query('content > :leave', animateChild(), {optional: true}),
query('content > :enter', animateChild(), {optional: true})
])
])
]),
@@ -236,7 +240,7 @@ export const fuseAnimations = [
trigger('routerTransitionRight', [
transition('* => *', [
query('fuse-content > :enter, fuse-content > :leave', [
query('content > :enter, content > :leave', [
style({
position: 'absolute',
top : 0,
@@ -245,7 +249,7 @@ export const fuseAnimations = [
right : 0
})
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({
transform: 'translateX(-100%)',
opacity : 0
@@ -253,7 +257,7 @@ export const fuseAnimations = [
], {optional: true}),
sequence([
group([
query('fuse-content > :leave', [
query('content > :leave', [
style({
transform: 'translateX(0)',
opacity : 1
@@ -264,7 +268,7 @@ export const fuseAnimations = [
opacity : 0
}))
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({transform: 'translateX(-100%)'}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
@@ -273,8 +277,8 @@ export const fuseAnimations = [
}))
], {optional: true})
]),
query('fuse-content > :leave', animateChild(), {optional: true}),
query('fuse-content > :enter', animateChild(), {optional: true})
query('content > :leave', animateChild(), {optional: true}),
query('content > :enter', animateChild(), {optional: true})
])
])
]),
@@ -282,7 +286,7 @@ export const fuseAnimations = [
trigger('routerTransitionUp', [
transition('* => *', [
query('fuse-content > :enter, fuse-content > :leave', [
query('content > :enter, content > :leave', [
style({
position: 'absolute',
top : 0,
@@ -291,14 +295,14 @@ export const fuseAnimations = [
right : 0
})
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({
transform: 'translateY(100%)',
opacity : 0
})
], {optional: true}),
group([
query('fuse-content > :leave', [
query('content > :leave', [
style({
transform: 'translateY(0)',
opacity : 1
@@ -309,7 +313,7 @@ export const fuseAnimations = [
opacity : 0
}))
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({transform: 'translateY(100%)'}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
@@ -318,15 +322,15 @@ export const fuseAnimations = [
}))
], {optional: true})
]),
query('fuse-content > :leave', animateChild(), {optional: true}),
query('fuse-content > :enter', animateChild(), {optional: true})
query('content > :leave', animateChild(), {optional: true}),
query('content > :enter', animateChild(), {optional: true})
])
]),
trigger('routerTransitionDown', [
transition('* => *', [
query('fuse-content > :enter, fuse-content > :leave', [
query('content > :enter, content > :leave', [
style({
position: 'absolute',
top : 0,
@@ -335,7 +339,7 @@ export const fuseAnimations = [
right : 0
})
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({
transform: 'translateY(-100%)',
opacity : 0
@@ -343,7 +347,7 @@ export const fuseAnimations = [
], {optional: true}),
sequence([
group([
query('fuse-content > :leave', [
query('content > :leave', [
style({
transform: 'translateY(0)',
opacity : 1
@@ -354,7 +358,7 @@ export const fuseAnimations = [
opacity : 0
}))
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({transform: 'translateY(-100%)'}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
@@ -363,8 +367,8 @@ export const fuseAnimations = [
}))
], {optional: true})
]),
query('fuse-content > :leave', animateChild(), {optional: true}),
query('fuse-content > :enter', animateChild(), {optional: true})
query('content > :leave', animateChild(), {optional: true}),
query('content > :enter', animateChild(), {optional: true})
])
])
]),
@@ -373,7 +377,7 @@ export const fuseAnimations = [
transition('* => *', group([
query('fuse-content > :enter, fuse-content > :leave ', [
query('content > :enter, content > :leave ', [
style({
position: 'absolute',
top : 0,
@@ -383,12 +387,12 @@ export const fuseAnimations = [
})
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({
opacity: 0
})
], {optional: true}),
query('fuse-content > :leave', [
query('content > :leave', [
style({
opacity: 1
}),
@@ -397,7 +401,7 @@ export const fuseAnimations = [
opacity: 0
}))
], {optional: true}),
query('fuse-content > :enter', [
query('content > :enter', [
style({
opacity: 0
}),
@@ -406,8 +410,8 @@ export const fuseAnimations = [
opacity: 1
}))
], {optional: true}),
query('fuse-content > :enter', animateChild(), {optional: true}),
query('fuse-content > :leave', animateChild(), {optional: true})
query('content > :enter', animateChild(), {optional: true}),
query('content > :leave', animateChild(), {optional: true})
]))
])
];

View File

@@ -10,7 +10,14 @@ export class FuseConfirmDialogComponent
{
public confirmMessage: string;
constructor(public dialogRef: MatDialogRef<FuseConfirmDialogComponent>)
/**
* Constructor
*
* @param {MatDialogRef<FuseConfirmDialogComponent>} dialogRef
*/
constructor(
public dialogRef: MatDialogRef<FuseConfirmDialogComponent>
)
{
}

View File

@@ -1,8 +1,6 @@
import { Component, Input, OnInit } from '@angular/core';
import { interval } from 'rxjs';
import { map } from 'rxjs/operators';
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { interval, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import * as moment from 'moment';
@Component({
@@ -10,46 +8,82 @@ import * as moment from 'moment';
templateUrl: './countdown.component.html',
styleUrls : ['./countdown.component.scss']
})
export class FuseCountdownComponent implements OnInit
export class FuseCountdownComponent implements OnInit, OnDestroy
{
@Input('eventDate') eventDate;
// Event date
@Input('eventDate')
eventDate;
countdown: any;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*/
constructor()
{
// Set the defaults
this.countdown = {
days : '',
hours : '',
minutes: '',
seconds: ''
};
// Set the private defaults
this._unsubscribeAll = new Subject();
}
ngOnInit()
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
const currDate = moment();
const eventDate = moment(this.eventDate);
// Get the difference in between the current date and event date
let diff = eventDate.diff(currDate, 'seconds');
const countDown = interval(1000).pipe(
map(value => {
return diff = diff - 1;
}),
map(value => {
const timeLeft = moment.duration(value, 'seconds');
// Create a subscribable interval
const countDown = interval(1000)
.pipe(
map(value => {
return diff = diff - 1;
}),
map(value => {
const timeLeft = moment.duration(value, 'seconds');
return {
days : timeLeft.asDays().toFixed(0),
hours : timeLeft.hours(),
minutes: timeLeft.minutes(),
seconds: timeLeft.seconds()
};
})
);
return {
days : timeLeft.asDays().toFixed(0),
hours : timeLeft.hours(),
minutes: timeLeft.minutes(),
seconds: timeLeft.seconds()
};
})
);
countDown.subscribe(value => {
this.countdown = value;
});
// Subscribe to the countdown interval
countDown
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(value => {
this.countdown = value;
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
}

View File

@@ -7,6 +7,9 @@ import { Component } from '@angular/core';
})
export class FuseDemoContentComponent
{
/**
* Constructor
*/
constructor()
{
}

View File

@@ -1,99 +1,99 @@
<div class="demo-sidenav">
<div class="demo-sidebar">
<mat-list>
<h3 matSubheader>Sidenav Demo</h3>
<h3 matSubheader>Sidebar Demo</h3>
<mat-list-item>
<span>Sidenav Item 1</span>
<span>Sidebar Item 1</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 2</span>
<span>Sidebar Item 2</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 3</span>
<span>Sidebar Item 3</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 4</span>
<span>Sidebar Item 4</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 5</span>
<span>Sidebar Item 5</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 6</span>
<span>Sidebar Item 6</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 7</span>
<span>Sidebar Item 7</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 8</span>
<span>Sidebar Item 8</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 9</span>
<span>Sidebar Item 9</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 10</span>
<span>Sidebar Item 10</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 11</span>
<span>Sidebar Item 11</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 12</span>
<span>Sidebar Item 12</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 13</span>
<span>Sidebar Item 13</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 14</span>
<span>Sidebar Item 14</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 15</span>
<span>Sidebar Item 15</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidenav Item 16</span>
<span>Sidebar Item 16</span>
</mat-list-item>
</mat-list>

View File

@@ -0,0 +1,16 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-demo-sidebar',
templateUrl: './demo-sidebar.component.html',
styleUrls : ['./demo-sidebar.component.scss']
})
export class FuseDemoSidebarComponent
{
/**
* Constructor
*/
constructor()
{
}
}

View File

@@ -1,13 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-demo-sidenav',
templateUrl: './demo-sidenav.component.html',
styleUrls : ['./demo-sidenav.component.scss']
})
export class FuseDemoSidenavComponent
{
constructor()
{
}
}

View File

@@ -4,12 +4,12 @@ import { RouterModule } from '@angular/router';
import { MatDividerModule, MatListModule } from '@angular/material';
import { FuseDemoContentComponent } from './demo-content/demo-content.component';
import { FuseDemoSidenavComponent } from './demo-sidenav/demo-sidenav.component';
import { FuseDemoSidebarComponent } from './demo-sidebar/demo-sidebar.component';
@NgModule({
declarations: [
FuseDemoContentComponent,
FuseDemoSidenavComponent
FuseDemoSidebarComponent
],
imports : [
RouterModule,
@@ -19,7 +19,7 @@ import { FuseDemoSidenavComponent } from './demo-sidenav/demo-sidenav.component'
],
exports : [
FuseDemoContentComponent,
FuseDemoSidenavComponent
FuseDemoSidebarComponent
]
})
export class FuseDemoModule

View File

@@ -3,4 +3,5 @@
padding: 8px;
background: #263238;
cursor: text;
overflow: auto;
}

View File

@@ -1,28 +1,55 @@
import { Component, ContentChild, ElementRef, Input, OnInit } from '@angular/core';
import { Component, ContentChild, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import * as Prism from 'prismjs/prism';
import './prism-languages';
import '@fuse/components/highlight/prism-languages';
@Component({
selector : 'fuse-highlight',
template : ' ',
template : '',
styleUrls: ['./highlight.component.scss']
})
export class FuseHighlightComponent implements OnInit
export class FuseHighlightComponent implements OnInit, OnDestroy
{
@ContentChild('source') source: ElementRef;
@Input('lang') lang: string;
@Input('path') path: string;
// Source
@ContentChild('source')
source: ElementRef;
// Lang
@Input('lang')
lang: string;
// Path
@Input('path')
path: string;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {ElementRef} _elementRef
* @param {HttpClient} _httpClient
*/
constructor(
private elementRef: ElementRef,
private http: HttpClient
private _elementRef: ElementRef,
private _httpClient: HttpClient
)
{
// Set the private defaults
this._unsubscribeAll = new Subject();
}
ngOnInit()
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// If there is no language defined, return...
if ( !this.lang )
@@ -34,11 +61,13 @@ export class FuseHighlightComponent implements OnInit
if ( this.path )
{
// Get the source
this.http.get(this.path, {responseType: 'text'}).subscribe((response) => {
this._httpClient.get(this.path, {responseType: 'text'})
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((response) => {
// Highlight it
this.highlight(response);
});
// Highlight it
this.highlight(response);
});
}
// If the path is not defined and the source element exists...
@@ -49,7 +78,26 @@ export class FuseHighlightComponent implements OnInit
}
}
highlight(sourceCode)
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Highlight the given source code
*
* @param sourceCode
*/
highlight(sourceCode): void
{
// Split the source into lines
const sourceLines = sourceCode.split('\n');
@@ -94,9 +142,8 @@ export class FuseHighlightComponent implements OnInit
const highlightedCode = Prism.highlight(source, Prism.languages[this.lang]);
// Replace the innerHTML of the component with the highlighted code
this.elementRef.nativeElement.innerHTML =
this._elementRef.nativeElement.innerHTML =
'<pre><code class="highlight language-' + this.lang + '">' + highlightedCode + '</code></pre>';
}
}

View File

@@ -1,4 +1,5 @@
import 'prismjs/prism';
import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-c';
import 'prismjs/components/prism-cpp';
import 'prismjs/components/prism-csharp';

View File

@@ -13,22 +13,78 @@ import { MatColors } from '@fuse/mat-colors';
export class FuseMaterialColorPickerComponent implements OnChanges
{
colors: any;
selectedColor: any;
hues: string[];
view = 'palettes';
selectedColor: any;
view: string;
@Input() selectedPalette = '';
@Input() selectedHue = '';
@Input() selectedFg = '';
@Input() value: any;
@Output() onValueChange = new EventEmitter();
@Output() selectedPaletteChange = new EventEmitter();
@Output() selectedHueChange = new EventEmitter();
@Output() selectedClassChange = new EventEmitter();
@Output() selectedBgChange = new EventEmitter();
@Output() selectedFgChange = new EventEmitter();
@Input()
selectedPalette: string;
_selectedClass = '';
@Input()
selectedHue: string;
@Input()
selectedFg: string;
@Input()
value: any;
@Output()
onValueChange: EventEmitter<any>;
@Output()
selectedPaletteChange: EventEmitter<any>;
@Output()
selectedHueChange: EventEmitter<any>;
@Output()
selectedClassChange: EventEmitter<any>;
@Output()
selectedBgChange: EventEmitter<any>;
@Output()
selectedFgChange: EventEmitter<any>;
// Private
_selectedClass: string;
_selectedBg: string;
/**
* Constructor
*/
constructor()
{
// Set the defaults
this.colors = MatColors.all;
this.hues = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', 'A100', 'A200', 'A400', 'A700'];
this.selectedFg = '';
this.selectedHue = '';
this.selectedPalette = '';
this.view = 'palettes';
this.onValueChange = new EventEmitter();
this.selectedPaletteChange = new EventEmitter();
this.selectedHueChange = new EventEmitter();
this.selectedClassChange = new EventEmitter();
this.selectedBgChange = new EventEmitter();
this.selectedFgChange = new EventEmitter();
// Set the private defaults
this._selectedClass = '';
this._selectedBg = '';
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Selected class
*
* @param value
*/
@Input()
set selectedClass(value)
{
@@ -54,7 +110,11 @@ export class FuseMaterialColorPickerComponent implements OnChanges
return this._selectedClass;
}
_selectedBg = '';
/**
* Selected bg
*
* @param value
*/
@Input()
set selectedBg(value)
{
@@ -86,13 +146,16 @@ export class FuseMaterialColorPickerComponent implements OnChanges
return this._selectedBg;
}
constructor()
{
this.colors = MatColors.all;
this.hues = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', 'A100', 'A200', 'A400', 'A700'];
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
ngOnChanges(changes: any)
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: any): void
{
if ( changes.selectedBg && changes.selectedBg.currentValue === '' ||
changes.selectedClass && changes.selectedClass.currentValue === '' ||
@@ -106,21 +169,38 @@ export class FuseMaterialColorPickerComponent implements OnChanges
this.updateSelectedColor();
}
}
selectPalette(palette)
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Select palette
*
* @param palette
*/
selectPalette(palette): void
{
this.selectedPalette = palette;
this.updateSelectedColor();
this.view = 'hues';
}
selectHue(hue)
/**
* Select hue
*
* @param hue
*/
selectHue(hue): void
{
this.selectedHue = hue;
this.updateSelectedColor();
}
removeColor()
/**
* Remove color
*/
removeColor(): void
{
this.selectedPalette = '';
this.selectedHue = '';
@@ -128,7 +208,10 @@ export class FuseMaterialColorPickerComponent implements OnChanges
this.view = 'palettes';
}
updateSelectedColor()
/**
* Update selected color
*/
updateSelectedColor(): void
{
setTimeout(() => {
@@ -168,12 +251,18 @@ export class FuseMaterialColorPickerComponent implements OnChanges
});
}
backToPaletteSelection()
/**
* Go back to palette selection
*/
backToPaletteSelection(): void
{
this.view = 'palettes';
}
onMenuOpen()
/**
* On menu open
*/
onMenuOpen(): void
{
if ( this.selectedPalette === '' )
{

View File

@@ -1,7 +1,6 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule, MatIconModule, MatMenuModule, MatRippleModule } from '@angular/material';
import { FusePipesModule } from '@fuse/pipes/pipes.module';

View File

@@ -0,0 +1,69 @@
<ng-container *ngIf="!item.hidden">
<!-- normal collapse -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="!item.url && !item.function" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.url -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && !item.externalUrl && !item.function"
[routerLink]="[item.url]" [routerLinkActive]="['active', 'mat-accent-bg']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
[target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.externalUrl -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && item.externalUrl && !item.function"
[href]="item.url" [target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" [ngClass]="item.classes" *ngIf="!item.url && item.function"
(click)="item.function()" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</span>
<!-- item.url && item.function -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && !item.externalUrl && item.function"
(click)="item.function()"
[routerLink]="[item.url]" [routerLinkActive]="['active', 'mat-accent-bg']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.externalUrl && item.function -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && item.externalUrl && item.function"
(click)="item.function()"
[href]="item.url" [target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<ng-template #itemContent>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
<mat-icon class="collapsable-arrow">keyboard_arrow_right</mat-icon>
</ng-template>
<div class="children" [ngClass]="{'open': isOpen}">
<div class="{{fuseConfig.layout.navbar.background}}">
<ng-container *ngFor="let item of item.children">
<fuse-nav-horizontal-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-horizontal-item>
<fuse-nav-horizontal-collapsable *ngIf="item.type=='collapsable'"
[item]="item"></fuse-nav-horizontal-collapsable>
<fuse-nav-horizontal-collapsable *ngIf="item.type=='group'"
[item]="item"></fuse-nav-horizontal-collapsable>
</ng-container>
</div>
</div>
</ng-container>

View File

@@ -0,0 +1,86 @@
import { Component, HostBinding, HostListener, Input, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations';
import { FuseConfigService } from '@fuse/services/config.service';
@Component({
selector : 'fuse-nav-horizontal-collapsable',
templateUrl: './collapsable.component.html',
styleUrls : ['./collapsable.component.scss'],
animations : fuseAnimations
})
export class FuseNavHorizontalCollapsableComponent implements OnInit, OnDestroy
{
fuseConfig: any;
isOpen = false;
@HostBinding('class')
classes = 'nav-collapsable nav-item';
@Input()
item: any;
// Private
private _unsubscribeAll: Subject<any>;
constructor(
private _fuseConfigService: FuseConfigService
)
{
// Set the private defaults
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Subscribe to config changes
this._fuseConfigService.config
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(
(config) => {
this.fuseConfig = config;
}
);
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open
*/
@HostListener('mouseenter')
open(): void
{
this.isOpen = true;
}
/**
* Close
*/
@HostListener('mouseleave')
close(): void
{
this.isOpen = false;
}
}

View File

@@ -0,0 +1,48 @@
<ng-container *ngIf="!item.hidden">
<!-- item.url -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && !item.externalUrl && !item.function"
[routerLink]="[item.url]" [routerLinkActive]="['active', 'mat-accent-bg']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
[target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.externalUrl -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && item.externalUrl && !item.function"
[href]="item.url" [target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" [ngClass]="item.classes" *ngIf="!item.url && item.function"
(click)="item.function()" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</span>
<!-- item.url && item.function -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && !item.externalUrl && item.function"
(click)="item.function()"
[routerLink]="[item.url]" [routerLinkActive]="['active', 'mat-accent-bg']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
[target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.externalUrl && item.function -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && item.externalUrl && item.function"
(click)="item.function()"
[href]="item.url" [target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<ng-template #itemContent>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
</ng-template>
</ng-container>

View File

@@ -0,0 +1,23 @@
import { Component, HostBinding, Input } from '@angular/core';
@Component({
selector : 'fuse-nav-horizontal-item',
templateUrl: './item.component.html',
styleUrls : ['./item.component.scss']
})
export class FuseNavHorizontalItemComponent
{
@HostBinding('class')
classes = 'nav-item';
@Input()
item: any;
/**
* Constructor
*/
constructor()
{
}
}

View File

@@ -1,52 +0,0 @@
<ng-container *ngIf="!item.hidden">
<!-- normal collapse -->
<a class="nav-link" *ngIf="!item.url && !item.function" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.url -->
<a class="nav-link" *ngIf="item.url && !item.function"
[routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" *ngIf="!item.url && item.function" (click)="item.function()" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</span>
<!-- item.url && item.function -->
<a class="nav-link" *ngIf="item.url && item.function" (click)="item.function()"
[routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<ng-template #itemContent>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
<mat-icon class="collapse-arrow">keyboard_arrow_right</mat-icon>
</ng-template>
<div class="children" [ngClass]="{'open': isOpen}">
<div class="{{fuseSettings.colorClasses.navbar}}">
<ng-container *ngFor="let item of item.children">
<fuse-nav-horizontal-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-horizontal-item>
<fuse-nav-horizontal-collapse *ngIf="item.type=='collapse'"
[item]="item"></fuse-nav-horizontal-collapse>
<fuse-nav-horizontal-collapse *ngIf="item.type=='group'" [item]="item"></fuse-nav-horizontal-collapse>
</ng-container>
</div>
</div>
</ng-container>

View File

@@ -1,51 +0,0 @@
import { Component, HostBinding, HostListener, Input, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { fuseAnimations } from '../../../../animations/index';
import { FuseConfigService } from '../../../../services/config.service';
@Component({
selector : 'fuse-nav-horizontal-collapse',
templateUrl: './nav-horizontal-collapse.component.html',
styleUrls : ['./nav-horizontal-collapse.component.scss'],
animations : fuseAnimations
})
export class FuseNavHorizontalCollapseComponent implements OnDestroy
{
onConfigChanged: Subscription;
fuseSettings: any;
isOpen = false;
@HostBinding('class') classes = 'nav-item nav-collapse';
@Input() item: any;
@HostListener('mouseenter')
open()
{
this.isOpen = true;
}
@HostListener('mouseleave')
close()
{
this.isOpen = false;
}
constructor(
private fuseConfig: FuseConfigService
)
{
this.onConfigChanged =
this.fuseConfig.onConfigChanged
.subscribe(
(newSettings) => {
this.fuseSettings = newSettings;
}
);
}
ngOnDestroy()
{
this.onConfigChanged.unsubscribe();
}
}

View File

@@ -1,30 +0,0 @@
<ng-container *ngIf="!item.hidden">
<!-- item.url -->
<a class="nav-link" *ngIf="item.url" [routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</span>
<!-- item.url && item.function -->
<a class="nav-link" *ngIf="item.url && item.function" (click)="item.function()"
[routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<ng-template #itemContent>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
</ng-template>
</ng-container>

View File

@@ -1,12 +0,0 @@
import { Component, HostBinding, Input } from '@angular/core';
@Component({
selector : 'fuse-nav-horizontal-item',
templateUrl: './nav-horizontal-item.component.html',
styleUrls : ['./nav-horizontal-item.component.scss']
})
export class FuseNavHorizontalItemComponent
{
@HostBinding('class') classes = 'nav-item';
@Input() item: any;
}

View File

@@ -1,5 +1,4 @@
<div id="main-navigation" class="nav"
[ngClass]="{'horizontal':layout === 'horizontal', 'vertical':layout === 'vertical'}">
<div class="nav" [ngClass]="{'horizontal':layout === 'horizontal', 'vertical':layout === 'vertical'}">
<!-- Vertical Navigation Layout -->
<ng-container *ngIf="layout === 'vertical'">
@@ -7,7 +6,7 @@
<ng-container *ngFor="let item of navigation">
<fuse-nav-vertical-group *ngIf="item.type=='group'" [item]="item"></fuse-nav-vertical-group>
<fuse-nav-vertical-collapse *ngIf="item.type=='collapse'" [item]="item"></fuse-nav-vertical-collapse>
<fuse-nav-vertical-collapsable *ngIf="item.type=='collapse'" [item]="item"></fuse-nav-vertical-collapsable>
<fuse-nav-vertical-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-vertical-item>
</ng-container>
@@ -20,8 +19,9 @@
<ng-container *ngFor="let item of navigation">
<fuse-nav-horizontal-collapse *ngIf="item.type=='group'" [item]="item"></fuse-nav-horizontal-collapse>
<fuse-nav-horizontal-collapse *ngIf="item.type=='collapse'" [item]="item"></fuse-nav-horizontal-collapse>
<fuse-nav-horizontal-collapsable *ngIf="item.type=='group'" [item]="item"></fuse-nav-horizontal-collapsable>
<fuse-nav-horizontal-collapsable *ngIf="item.type=='collapse'"
[item]="item"></fuse-nav-horizontal-collapsable>
<fuse-nav-horizontal-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-horizontal-item>
</ng-container>

View File

@@ -4,7 +4,7 @@ fuse-navigation {
display: flex;
flex: 1 0 auto;
#main-navigation {
> .nav {
margin: 0;
padding: 0;
width: 100%;

View File

@@ -1,4 +1,8 @@
import { Component, Input, ViewEncapsulation } from '@angular/core';
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
@Component({
selector : 'fuse-navigation',
@@ -6,13 +10,45 @@ import { Component, Input, ViewEncapsulation } from '@angular/core';
styleUrls : ['./navigation.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseNavigationComponent
export class FuseNavigationComponent implements OnInit
{
@Input() layout = 'vertical';
@Input() navigation: any;
@Input()
layout = 'vertical';
constructor()
@Input()
navigation: any;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*/
constructor(
private _fuseNavigationService: FuseNavigationService
)
{
// Set the private defaults
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Load the navigation either from the input or from the service
this.navigation = this.navigation || this._fuseNavigationService.getCurrentNavigation();
// Subscribe to the current navigation changes
this._fuseNavigationService.onNavigationChanged
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(() => {
this.navigation = this._fuseNavigationService.getCurrentNavigation();
});
}
}

View File

@@ -6,11 +6,11 @@ import { MatIconModule, MatRippleModule } from '@angular/material';
import { TranslateModule } from '@ngx-translate/core';
import { FuseNavigationComponent } from './navigation.component';
import { FuseNavVerticalItemComponent } from './vertical/nav-item/nav-vertical-item.component';
import { FuseNavVerticalCollapseComponent } from './vertical/nav-collapse/nav-vertical-collapse.component';
import { FuseNavVerticalGroupComponent } from './vertical/nav-group/nav-vertical-group.component';
import { FuseNavHorizontalItemComponent } from './horizontal/nav-item/nav-horizontal-item.component';
import { FuseNavHorizontalCollapseComponent } from './horizontal/nav-collapse/nav-horizontal-collapse.component';
import { FuseNavVerticalItemComponent } from './vertical/item/item.component';
import { FuseNavVerticalCollapsableComponent } from './vertical/collapsable/collapsable.component';
import { FuseNavVerticalGroupComponent } from './vertical/group/group.component';
import { FuseNavHorizontalItemComponent } from './horizontal/item/item.component';
import { FuseNavHorizontalCollapsableComponent } from './horizontal/collapsable/collapsable.component';
@NgModule({
imports : [
@@ -29,9 +29,9 @@ import { FuseNavHorizontalCollapseComponent } from './horizontal/nav-collapse/na
FuseNavigationComponent,
FuseNavVerticalGroupComponent,
FuseNavVerticalItemComponent,
FuseNavVerticalCollapseComponent,
FuseNavVerticalCollapsableComponent,
FuseNavHorizontalItemComponent,
FuseNavHorizontalCollapseComponent
FuseNavHorizontalCollapsableComponent
]
})
export class FuseNavigationModule

View File

@@ -1,48 +1,350 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
@Injectable()
import { FuseNavigationItem } from '@fuse/types';
@Injectable({
providedIn: 'root'
})
export class FuseNavigationService
{
flatNavigation: any[] = [];
onItemCollapsed: Subject<any>;
onItemCollapseToggled: Subject<any>;
onItemCollapsed: Subject<any> = new Subject;
onItemCollapseToggled: Subject<any> = new Subject;
// Private
private _onNavigationChanged: BehaviorSubject<any>;
private _onNavigationRegistered: BehaviorSubject<any>;
private _onNavigationUnregistered: BehaviorSubject<any>;
private _currentNavigationKey: string;
private _registry: { [key: string]: any } = {};
/**
* Constructor
*/
constructor()
{
// Set the defaults
this.onItemCollapsed = new Subject();
this.onItemCollapseToggled = new Subject();
// Set the private defaults
this._currentNavigationKey = null;
this._onNavigationChanged = new BehaviorSubject(null);
this._onNavigationRegistered = new BehaviorSubject(null);
this._onNavigationUnregistered = new BehaviorSubject(null);
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Get onNavigationChanged
*
* @returns {Observable<any>}
*/
get onNavigationChanged(): Observable<any>
{
return this._onNavigationChanged.asObservable();
}
/**
* Get onNavigationRegistered
*
* @returns {Observable<any>}
*/
get onNavigationRegistered(): Observable<any>
{
return this._onNavigationRegistered.asObservable();
}
/**
* Get onNavigationUnregistered
*
* @returns {Observable<any>}
*/
get onNavigationUnregistered(): Observable<any>
{
return this._onNavigationUnregistered.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Register the given navigation
* with the given key
*
* @param key
* @param navigation
*/
register(key, navigation): void
{
// Check if the key already being used
if ( this._registry[key] )
{
console.error(`The navigation with the key '${key}' already exists. Either unregister it first or use a unique key.`);
return;
}
// Add to the registry
this._registry[key] = navigation;
// Notify the subject
this._onNavigationRegistered.next([key, navigation]);
}
/**
* Unregister the navigation from the registry
* @param key
*/
unregister(key): void
{
// Check if the navigation exists
if ( !this._registry[key] )
{
console.warn(`The navigation with the key '${key}' doesn't exist in the registry.`);
}
// Unregister the sidebar
delete this._registry[key];
// Notify the subject
this._onNavigationUnregistered.next(key);
}
/**
* Get navigation from registry by key
*
* @param key
* @returns {any}
*/
getNavigation(key): any
{
// Check if the navigation exists
if ( !this._registry[key] )
{
console.warn(`The navigation with the key '${key}' doesn't exist in the registry.`);
return;
}
// Return the sidebar
return this._registry[key];
}
/**
* Get flattened navigation array
*
* @param navigation
* @param flatNavigation
* @returns {any[]}
*/
getFlatNavigation(navigation)
getFlatNavigation(navigation, flatNavigation: FuseNavigationItem[] = []): any
{
for ( const navItem of navigation )
for ( const item of navigation )
{
if ( navItem.type === 'item' )
if ( item.type === 'item' )
{
this.flatNavigation.push({
title: navItem.title,
type : navItem.type,
icon : navItem.icon || false,
url : navItem.url
});
flatNavigation.push(item);
continue;
}
if ( navItem.type === 'collapse' || navItem.type === 'group' )
if ( item.type === 'collapsable' || item.type === 'group' )
{
if ( navItem.children )
if ( item.children )
{
this.getFlatNavigation(navItem.children);
this.getFlatNavigation(item.children, flatNavigation);
}
}
}
return this.flatNavigation;
return flatNavigation;
}
/**
* Get the current navigation
*
* @returns {any}
*/
getCurrentNavigation(): any
{
if ( !this._currentNavigationKey )
{
console.warn(`The current navigation is not set.`);
return;
}
return this.getNavigation(this._currentNavigationKey);
}
/**
* Set the navigation with the key
* as the current navigation
*
* @param key
*/
setCurrentNavigation(key): void
{
// Check if the sidebar exists
if ( !this._registry[key] )
{
console.warn(`The navigation with the key '${key}' doesn't exist in the registry.`);
return;
}
// Set the current navigation key
this._currentNavigationKey = key;
// Notify the subject
this._onNavigationChanged.next(key);
}
/**
* Get navigation item by id from the
* current navigation
*
* @param id
* @param {any} navigation
* @returns {any | boolean}
*/
getNavigationItem(id, navigation = null): any | boolean
{
if ( !navigation )
{
navigation = this.getCurrentNavigation();
}
for ( const item of navigation )
{
if ( item.id === id )
{
return item;
}
if ( item.children )
{
const childItem = this.getNavigationItem(id, item.children);
if ( childItem )
{
return childItem;
}
}
}
return false;
}
/**
* Get the parent of the navigation item
* with the id
*
* @param id
* @param {any} navigation
* @param parent
*/
getNavigationItemParent(id, navigation = null, parent = null): any
{
if ( !navigation )
{
navigation = this.getCurrentNavigation();
parent = navigation;
}
for ( const item of navigation )
{
if ( item.id === id )
{
return parent;
}
if ( item.children )
{
const childItem = this.getNavigationItemParent(id, item.children, item);
if ( childItem )
{
return childItem;
}
}
}
return false;
}
/**
* Add a navigation item to the specified location
*
* @param item
* @param id
*/
addNavigationItem(item, id): void
{
// Get the current navigation
const navigation: any[] = this.getCurrentNavigation();
// Add to the end of the navigation
if ( id === 'end' )
{
navigation.push(item);
return;
}
// Add to the start of the navigation
if ( id === 'start' )
{
navigation.unshift(item);
}
// Add it to a specific location
const parent: any = this.getNavigationItem(id);
if ( parent )
{
// Check if parent has a children entry,
// and add it if it doesn't
if ( !parent.children )
{
parent.children = [];
}
// Add the item
parent.children.push(item);
}
}
/**
* Remove navigation item with the given id
*
* @param id
*/
removeNavigationItem(id): void
{
const item = this.getNavigationItem(id);
// Return, if there is not such an item
if ( !item )
{
return;
}
// Get the parent of the item
let parent = this.getNavigationItemParent(id);
// This check is required because of the first level
// of the navigation, since the first level is not
// inside the 'children' array
parent = parent.children || parent;
// Remove the item
parent.splice(parent.indexOf(item), 1);
}
}

View File

@@ -0,0 +1,65 @@
<ng-container *ngIf="!item.hidden">
<!-- normal collapse -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="!item.url && !item.function"
(click)="toggleOpen($event)" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.url -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && !item.externalUrl && !item.function"
(click)="toggleOpen($event)"
[routerLink]="[item.url]" [routerLinkActive]="['active', 'mat-accent-bg']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
[target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.externalUrl -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && item.externalUrl && !item.function"
(click)="toggleOpen($event)"
[href]="item.url" [target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" [ngClass]="item.classes" *ngIf="!item.url && item.function"
(click)="toggleOpen($event);item.function()" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</span>
<!-- item.url && item.function -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && !item.externalUrl && item.function"
(click)="toggleOpen($event);item.function()"
[routerLink]="[item.url]" [routerLinkActive]="['active', 'mat-accent-bg']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.externalUrl && item.function -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && item.externalUrl && item.function"
(click)="toggleOpen($event);item.function()"
[href]="item.url" [target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<ng-template #itemContent>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
<mat-icon class="collapsable-arrow">keyboard_arrow_right</mat-icon>
</ng-template>
<div class="children" [@slideInOut]="isOpen">
<ng-container *ngFor="let item of item.children">
<fuse-nav-vertical-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-vertical-item>
<fuse-nav-vertical-collapsable *ngIf="item.type=='collapsable'"
[item]="item"></fuse-nav-vertical-collapsable>
<fuse-nav-vertical-group *ngIf="item.type=='group'" [item]="item"></fuse-nav-vertical-group>
</ng-container>
</div>
</ng-container>

View File

@@ -20,7 +20,7 @@
.nav-link {
.collapse-arrow {
.collapsable-arrow {
transition: transform .3s ease-in-out, opacity .25s ease-in-out .1s;
transform: rotate(0);
}
@@ -34,13 +34,9 @@
> .nav-link {
.collapse-arrow {
.collapsable-arrow {
transform: rotate(90deg);
}
}
> .children {
}
}
}

View File

@@ -1,46 +1,79 @@
import { Component, HostBinding, Input, OnInit } from '@angular/core';
import { FuseNavigationService } from '../../navigation.service';
import { Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { fuseAnimations } from '../../../../animations/index';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { FuseNavigationItem } from '@fuse/types';
import { fuseAnimations } from '@fuse/animations';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
@Component({
selector : 'fuse-nav-vertical-collapse',
templateUrl: './nav-vertical-collapse.component.html',
styleUrls : ['./nav-vertical-collapse.component.scss'],
selector : 'fuse-nav-vertical-collapsable',
templateUrl: './collapsable.component.html',
styleUrls : ['./collapsable.component.scss'],
animations : fuseAnimations
})
export class FuseNavVerticalCollapseComponent implements OnInit
export class FuseNavVerticalCollapsableComponent implements OnInit, OnDestroy
{
@Input() item: any;
@HostBinding('class') classes = 'nav-collapse nav-item';
@HostBinding('class.open') public isOpen = false;
@Input()
item: FuseNavigationItem;
@HostBinding('class')
classes = 'nav-collapsable nav-item';
@HostBinding('class.open')
public isOpen = false;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {FuseNavigationService} _fuseNavigationService
* @param {Router} _router
*/
constructor(
private navigationService: FuseNavigationService,
private router: Router
private _fuseNavigationService: FuseNavigationService,
private _router: Router
)
{
// Listen for route changes
router.events.subscribe(
(event) => {
if ( event instanceof NavigationEnd )
// Set the private defaults
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Listen for router events
this._router.events
.pipe(
filter(event => event instanceof NavigationEnd),
takeUntil(this._unsubscribeAll)
)
.subscribe((event: NavigationEnd) => {
// Check if the url can be found in
// one of the children of this item
if ( this.isUrlInChildren(this.item, event.urlAfterRedirects) )
{
// Check if the url can be found in
// one of the children of this item
if ( this.isUrlInChildren(this.item, event.urlAfterRedirects) )
{
this.expand();
}
else
{
this.collapse();
}
this.expand();
}
}
);
else
{
this.collapse();
}
});
// Listen for collapsing of any navigation item
this.navigationService.onItemCollapsed
this._fuseNavigationService.onItemCollapsed
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(
(clickedItem) => {
if ( clickedItem && clickedItem.children )
@@ -54,7 +87,7 @@ export class FuseNavVerticalCollapseComponent implements OnInit
// Check if the url can be found in
// one of the children of this item
if ( this.isUrlInChildren(this.item, this.router.url) )
if ( this.isUrlInChildren(this.item, this._router.url) )
{
return;
}
@@ -67,13 +100,10 @@ export class FuseNavVerticalCollapseComponent implements OnInit
}
}
);
}
ngOnInit()
{
// Check if the url can be found in
// one of the children of this item
if ( this.isUrlInChildren(this.item, this.router.url) )
if ( this.isUrlInChildren(this.item, this._router.url) )
{
this.expand();
}
@@ -83,26 +113,40 @@ export class FuseNavVerticalCollapseComponent implements OnInit
}
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle collapse
*
* @param ev
*/
toggleOpen(ev)
toggleOpen(ev): void
{
ev.preventDefault();
this.isOpen = !this.isOpen;
// Navigation collapse toggled...
this.navigationService.onItemCollapsed.next(this.item);
this.navigationService.onItemCollapseToggled.next();
this._fuseNavigationService.onItemCollapsed.next(this.item);
this._fuseNavigationService.onItemCollapseToggled.next();
}
/**
* Expand the collapsable navigation
*/
expand()
expand(): void
{
if ( this.isOpen )
{
@@ -110,13 +154,13 @@ export class FuseNavVerticalCollapseComponent implements OnInit
}
this.isOpen = true;
this.navigationService.onItemCollapseToggled.next();
this._fuseNavigationService.onItemCollapseToggled.next();
}
/**
* Collapse the collapsable navigation
*/
collapse()
collapse(): void
{
if ( !this.isOpen )
{
@@ -124,7 +168,7 @@ export class FuseNavVerticalCollapseComponent implements OnInit
}
this.isOpen = false;
this.navigationService.onItemCollapseToggled.next();
this._fuseNavigationService.onItemCollapseToggled.next();
}
/**
@@ -133,9 +177,9 @@ export class FuseNavVerticalCollapseComponent implements OnInit
*
* @param parent
* @param item
* @return {any}
* @returns {boolean}
*/
isChildrenOf(parent, item)
isChildrenOf(parent, item): boolean
{
if ( !parent.children )
{
@@ -162,9 +206,9 @@ export class FuseNavVerticalCollapseComponent implements OnInit
*
* @param parent
* @param url
* @returns {any}
* @returns {boolean}
*/
isUrlInChildren(parent, url)
isUrlInChildren(parent, url): boolean
{
if ( !parent.children )
{

View File

@@ -1,13 +1,14 @@
<ng-container *ngIf="!item.hidden">
<div class="group-title">
<div class="group-title" [ngClass]="item.classes">
<span class="hint-text" [translate]="item.translate">{{ item.title }}</span>
</div>
<div class="group-items">
<ng-container *ngFor="let item of item.children">
<fuse-nav-vertical-group *ngIf="item.type=='group'" [item]="item"></fuse-nav-vertical-group>
<fuse-nav-vertical-collapse *ngIf="item.type=='collapse'" [item]="item"></fuse-nav-vertical-collapse>
<fuse-nav-vertical-collapsable *ngIf="item.type=='collapsable'"
[item]="item"></fuse-nav-vertical-collapsable>
<fuse-nav-vertical-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-vertical-item>
</ng-container>
</div>

View File

@@ -0,0 +1,25 @@
import { Component, HostBinding, Input } from '@angular/core';
import { FuseNavigationItem } from '@fuse/types';
@Component({
selector : 'fuse-nav-vertical-group',
templateUrl: './group.component.html',
styleUrls : ['./group.component.scss']
})
export class FuseNavVerticalGroupComponent
{
@HostBinding('class')
classes = 'nav-group nav-item';
@Input()
item: FuseNavigationItem;
/**
* Constructor
*/
constructor()
{
}
}

View File

@@ -0,0 +1,48 @@
<ng-container *ngIf="!item.hidden">
<!-- item.url -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && !item.externalUrl && !item.function"
[routerLink]="[item.url]" [routerLinkActive]="['active', 'mat-accent-bg']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
[target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.externalUrl -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && item.externalUrl && !item.function"
[href]="item.url" [target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" [ngClass]="item.classes" *ngIf="!item.url && item.function"
(click)="item.function()" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</span>
<!-- item.url && item.function -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && !item.externalUrl && item.function"
(click)="item.function()"
[routerLink]="[item.url]" [routerLinkActive]="['active', 'mat-accent-bg']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
[target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.externalUrl && item.function -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="item.url && item.externalUrl && item.function"
(click)="item.function()"
[href]="item.url" [target]="item.openInNewTab ? '_blank' : '_self'" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<ng-template #itemContent>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
</ng-template>
</ng-container>

View File

@@ -0,0 +1,24 @@
import { Component, HostBinding, Input } from '@angular/core';
import { FuseNavigationItem } from '@fuse/types';
@Component({
selector : 'fuse-nav-vertical-item',
templateUrl: './item.component.html',
styleUrls : ['./item.component.scss']
})
export class FuseNavVerticalItemComponent
{
@HostBinding('class')
classes = 'nav-item';
@Input()
item: FuseNavigationItem;
/**
* Constructor
*/
constructor()
{
}
}

View File

@@ -1,45 +0,0 @@
<ng-container *ngIf="!item.hidden">
<!-- normal collapse -->
<a class="nav-link" *ngIf="!item.url && !item.function" (click)="toggleOpen($event)" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.url -->
<a class="nav-link" *ngIf="item.url && !item.function" (click)="toggleOpen($event)"
[routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" *ngIf="!item.url && item.function" (click)="toggleOpen($event);item.function()" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</span>
<!-- item.url && item.function -->
<a class="nav-link" *ngIf="item.url && item.function" (click)="toggleOpen($event);item.function()"
[routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<ng-template #itemContent>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
<mat-icon class="collapse-arrow">keyboard_arrow_right</mat-icon>
</ng-template>
<div class="children" [@slideInOut]="isOpen">
<ng-container *ngFor="let item of item.children">
<fuse-nav-vertical-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-vertical-item>
<fuse-nav-vertical-collapse *ngIf="item.type=='collapse'" [item]="item"></fuse-nav-vertical-collapse>
<fuse-nav-vertical-group *ngIf="item.type=='group'" [item]="item"></fuse-nav-vertical-group>
</ng-container>
</div>
</ng-container>

View File

@@ -1,17 +0,0 @@
import { Component, HostBinding, Input } from '@angular/core';
@Component({
selector : 'fuse-nav-vertical-group',
templateUrl: './nav-vertical-group.component.html',
styleUrls : ['./nav-vertical-group.component.scss']
})
export class FuseNavVerticalGroupComponent
{
@HostBinding('class') classes = 'nav-group nav-item';
@Input() item: any;
constructor()
{
}
}

View File

@@ -1,31 +0,0 @@
<ng-container *ngIf="!item.hidden">
<!-- item.url -->
<a class="nav-link" *ngIf="item.url && !item.function"
[routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" *ngIf="!item.url && item.function" (click)="item.function()" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</span>
<!-- item.url && item.function -->
<a class="nav-link" *ngIf="item.url && item.function" (click)="item.function()"
[routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<ng-template #itemContent>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
</ng-template>
</ng-container>

View File

@@ -1,16 +0,0 @@
import { Component, HostBinding, Input } from '@angular/core';
@Component({
selector : 'fuse-nav-vertical-item',
templateUrl: './nav-vertical-item.component.html',
styleUrls : ['./nav-vertical-item.component.scss']
})
export class FuseNavVerticalItemComponent
{
@HostBinding('class') classes = 'nav-item';
@Input() item: any;
constructor()
{
}
}

View File

@@ -1,11 +1,11 @@
<div class="fuse-search-bar" [ngClass]="{'expanded':!collapsed}">
<div class="fuse-search-bar-content" [ngClass]="toolbarColor">
<div class="fuse-search-bar-content" [ngClass]="fuseConfig.layout.toolbar.background">
<label for="fuse-search-bar-input">
<button mat-icon-button class="fuse-search-bar-expander" aria-label="Expand Search Bar" (click)="expand()"
*ngIf="collapsed">
<mat-icon class="s-24">search</mat-icon>
<mat-icon class="s-24 secondary-text">search</mat-icon>
</button>
<!--<span class="fuse-search-bar-loader" fxLayout="row" fxLayoutAlign="center center" *ngIf="!collapsed">
<mat-progress-spinner color="mat-accent" mode="indeterminate"></mat-progress-spinner>
@@ -17,7 +17,7 @@
<button mat-icon-button class="fuse-search-bar-collapser" (click)="collapse()"
aria-label="Collapse Search Bar">
<mat-icon class="s-24">close</mat-icon>
<mat-icon class="s-24 secondary-text">close</mat-icon>
</button>
</div>

View File

@@ -9,7 +9,7 @@
height: 64px;
font-size: 13px;
@include media-breakpoint-down('sm') {
@include media-breakpoint-down('xs') {
height: 56px;
}
@@ -28,7 +28,7 @@
height: 64px !important;
line-height: 64px !important;
@include media-breakpoint-down('sm') {
@include media-breakpoint-down('xs') {
height: 56px !important;
line-height: 56px !important;
}
@@ -39,7 +39,7 @@
height: 64px !important;
line-height: 64px !important;
@include media-breakpoint-down('sm') {
@include media-breakpoint-down('xs') {
height: 56px !important;
line-height: 56px !important;
}

View File

@@ -1,5 +1,6 @@
import { Component, EventEmitter, Output } from '@angular/core';
import { Subscription } from 'rxjs';
import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseConfigService } from '@fuse/services/config.service';
@@ -8,42 +9,91 @@ import { FuseConfigService } from '@fuse/services/config.service';
templateUrl: './search-bar.component.html',
styleUrls : ['./search-bar.component.scss']
})
export class FuseSearchBarComponent
export class FuseSearchBarComponent implements OnInit, OnDestroy
{
collapsed: boolean;
toolbarColor: string;
@Output() onInput: EventEmitter<any> = new EventEmitter();
onConfigChanged: Subscription;
fuseConfig: any;
@Output()
input: EventEmitter<any>;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {FuseConfigService} _fuseConfigService
*/
constructor(
private fuseConfig: FuseConfigService
private _fuseConfigService: FuseConfigService
)
{
// Set the defaults
this.input = new EventEmitter();
this.collapsed = true;
this.onConfigChanged =
this.fuseConfig.onConfigChanged
.subscribe(
(newSettings) => {
this.toolbarColor = newSettings.colorClasses.toolbar;
}
);
// Set the private defaults
this._unsubscribeAll = new Subject();
}
collapse()
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Subscribe to config changes
this._fuseConfigService.config
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(
(config) => {
this.fuseConfig = config;
}
);
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Collapse
*/
collapse(): void
{
this.collapsed = true;
}
expand()
/**
* Expand
*/
expand(): void
{
this.collapsed = false;
}
search(event)
/**
* Search
*
* @param event
*/
search(event): void
{
const value = event.target.value;
this.onInput.emit(value);
this.input.emit(event.target.value);
}
}

View File

@@ -7,7 +7,7 @@
</button>
</div>
<div class="shortcuts" fxHide fxShow.gt-sm [ngClass]="toolbarColor">
<div class="shortcuts" fxHide fxShow.gt-sm>
<div fxLayout="row" fxLayoutAlign="space-between center" fxFlex="0 1 auto">

View File

@@ -1,12 +1,11 @@
import { Component, ElementRef, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core';
import { ObservableMedia } from '@angular/flex-layout';
import { Subscription } from 'rxjs';
import { CookieService } from 'ngx-cookie-service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMatchMediaService } from '@fuse/services/match-media.service';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseConfigService } from '@fuse/services/config.service';
@Component({
selector : 'fuse-shortcuts',
@@ -15,48 +14,67 @@ import { FuseConfigService } from '@fuse/services/config.service';
})
export class FuseShortcutsComponent implements OnInit, OnDestroy
{
shortcutItems: any[] = [];
shortcutItems: any[];
navigationItems: any[];
filteredNavigationItems: any[];
searching = false;
mobileShortcutsPanelActive = false;
toolbarColor: string;
matchMediaSubscription: Subscription;
onConfigChanged: Subscription;
searching: boolean;
mobileShortcutsPanelActive: boolean;
@Input() navigation: any;
@Input()
navigation: any;
@ViewChild('searchInput') searchInputField;
@ViewChild('shortcuts') shortcutsEl: ElementRef;
@ViewChild('searchInput')
searchInputField;
@ViewChild('shortcuts')
shortcutsEl: ElementRef;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {Renderer2} _renderer
* @param {CookieService} _cookieService
* @param {FuseMatchMediaService} _fuseMatchMediaService
* @param {FuseNavigationService} _fuseNavigationService
* @param {ObservableMedia} _observableMedia
*/
constructor(
private renderer: Renderer2,
private observableMedia: ObservableMedia,
private fuseMatchMedia: FuseMatchMediaService,
private fuseNavigationService: FuseNavigationService,
private fuseConfig: FuseConfigService,
private cookieService: CookieService
private _cookieService: CookieService,
private _fuseMatchMediaService: FuseMatchMediaService,
private _fuseNavigationService: FuseNavigationService,
private _observableMedia: ObservableMedia,
private _renderer: Renderer2
)
{
this.onConfigChanged =
this.fuseConfig.onConfigChanged
.subscribe(
(newSettings) => {
this.toolbarColor = newSettings.colorClasses.toolbar;
}
);
// Set the defaults
this.shortcutItems = [];
this.searching = false;
this.mobileShortcutsPanelActive = false;
// Set the private defaults
this._unsubscribeAll = new Subject();
}
ngOnInit()
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the navigation items and flatten them
this.filteredNavigationItems = this.navigationItems = this.fuseNavigationService.getFlatNavigation(this.navigation);
this.filteredNavigationItems = this.navigationItems = this._fuseNavigationService.getFlatNavigation(this.navigation);
const cookieExists = this.cookieService.check('FUSE2.shortcuts');
const cookieExists = this._cookieService.check('FUSE2.shortcuts');
if ( cookieExists )
{
this.shortcutItems = JSON.parse(this.cookieService.get('FUSE2.shortcuts'));
this.shortcutItems = JSON.parse(this._cookieService.get('FUSE2.shortcuts'));
}
else
{
@@ -64,46 +82,61 @@ export class FuseShortcutsComponent implements OnInit, OnDestroy
this.shortcutItems = [
{
'title': 'Calendar',
'type' : 'nav-item',
'type' : 'item',
'icon' : 'today',
'url' : '/apps/calendar'
},
{
'title': 'Mail',
'type' : 'nav-item',
'type' : 'item',
'icon' : 'email',
'url' : '/apps/mail'
},
{
'title': 'Contacts',
'type' : 'nav-item',
'type' : 'item',
'icon' : 'account_box',
'url' : '/apps/contacts'
},
{
'title': 'To-Do',
'type' : 'nav-item',
'type' : 'item',
'icon' : 'check_box',
'url' : '/apps/todo'
}
];
}
this.matchMediaSubscription =
this.fuseMatchMedia.onMediaChange.subscribe(() => {
if ( this.observableMedia.isActive('gt-sm') )
this._fuseMatchMediaService.onMediaChange
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(() => {
if ( this._observableMedia.isActive('gt-sm') )
{
this.hideMobileShortcutsPanel();
}
});
}
ngOnDestroy()
/**
* On destroy
*/
ngOnDestroy(): void
{
this.matchMediaSubscription.unsubscribe();
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
search(event)
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Search
*
* @param event
*/
search(event): void
{
const value = event.target.value.toLowerCase();
@@ -122,7 +155,13 @@ export class FuseShortcutsComponent implements OnInit, OnDestroy
});
}
toggleShortcut(event, itemToToggle)
/**
* Toggle shortcut
*
* @param event
* @param itemToToggle
*/
toggleShortcut(event, itemToToggle): void
{
event.stopPropagation();
@@ -133,7 +172,7 @@ export class FuseShortcutsComponent implements OnInit, OnDestroy
this.shortcutItems.splice(i, 1);
// Save to the cookies
this.cookieService.set('FUSE2.shortcuts', JSON.stringify(this.shortcutItems));
this._cookieService.set('FUSE2.shortcuts', JSON.stringify(this.shortcutItems));
return;
}
@@ -142,32 +181,47 @@ export class FuseShortcutsComponent implements OnInit, OnDestroy
this.shortcutItems.push(itemToToggle);
// Save to the cookies
this.cookieService.set('FUSE2.shortcuts', JSON.stringify(this.shortcutItems));
this._cookieService.set('FUSE2.shortcuts', JSON.stringify(this.shortcutItems));
}
isInShortcuts(navigationItem)
/**
* Is in shortcuts?
*
* @param navigationItem
* @returns {any}
*/
isInShortcuts(navigationItem): any
{
return this.shortcutItems.find(item => {
return item.url === navigationItem.url;
});
}
onMenuOpen()
/**
* On menu open
*/
onMenuOpen(): void
{
setTimeout(() => {
this.searchInputField.nativeElement.focus();
});
}
showMobileShortcutsPanel()
/**
* Show mobile shortcuts
*/
showMobileShortcutsPanel(): void
{
this.mobileShortcutsPanelActive = true;
this.renderer.addClass(this.shortcutsEl.nativeElement, 'show-mobile-panel');
this._renderer.addClass(this.shortcutsEl.nativeElement, 'show-mobile-panel');
}
hideMobileShortcutsPanel()
/**
* Hide mobile shortcuts
*/
hideMobileShortcutsPanel(): void
{
this.mobileShortcutsPanelActive = false;
this.renderer.removeClass(this.shortcutsEl.nativeElement, 'show-mobile-panel');
this._renderer.removeClass(this.shortcutsEl.nativeElement, 'show-mobile-panel');
}
}

View File

@@ -1,3 +1,5 @@
@import "src/@fuse/scss/fuse";
fuse-sidebar {
display: flex;
flex-direction: column;
@@ -11,17 +13,21 @@ fuse-sidebar {
min-width: 280px;
max-width: 280px;
z-index: 1000;
transition-property: transform, width, min-width, max-width;
transition-duration: 150ms;
transition-timing-function: ease-in-out;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.35);
background: white;
&.left-aligned {
@include media-breakpoint-down('xs') {
min-width: 0 !important;
max-width: 80vw !important;
width: 80vw !important;
}
&.left-positioned {
left: 0;
transform: translateX(-100%);
}
&.right-aligned {
&.right-positioned {
right: 0;
transform: translateX(100%);
}
@@ -39,12 +45,12 @@ fuse-sidebar {
position: absolute !important;
top: 0;
bottom: 0;
}
&:not(.unfolded) {
width: 64px;
min-width: 64px;
max-width: 64px;
}
&.animations-enabled {
transition-property: transform, width, min-width, max-width;
transition-duration: 150ms;
transition-timing-function: ease-in-out;
}
}
@@ -54,7 +60,11 @@ fuse-sidebar {
bottom: 0;
left: 0;
right: 0;
z-index: 3;
z-index: 999;
background-color: rgba(0, 0, 0, 0.6);
opacity: 0;
&.fuse-sidebar-overlay-invisible {
background-color: transparent;
}
}

View File

@@ -1,7 +1,8 @@
import { Component, ElementRef, HostBinding, HostListener, Input, OnDestroy, OnInit, Renderer2, ViewEncapsulation } from '@angular/core';
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnDestroy, OnInit, Output, Renderer2, RendererStyleFlags2, ViewEncapsulation } from '@angular/core';
import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
import { ObservableMedia } from '@angular/flex-layout';
import { Subscription } from 'rxjs';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseSidebarService } from './sidebar.service';
import { FuseMatchMediaService } from '@fuse/services/match-media.service';
@@ -19,9 +20,13 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
@Input()
name: string;
// Align
// Key
@Input()
align: 'left' | 'right';
key: string;
// Position
@Input()
position: 'left' | 'right';
// Open
@HostBinding('class.open')
@@ -35,37 +40,118 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
@HostBinding('class.locked-open')
isLockedOpen: boolean;
// Folded
@HostBinding('class.folded')
// Folded width
@Input()
foldedWidth: number;
// Folded auto trigger on hover
@Input()
foldedAutoTriggerOnHover: boolean;
// Folded unfolded
@HostBinding('class.unfolded')
unfolded: boolean;
// Invisible overlay
@Input()
invisibleOverlay: boolean;
// Folded changed
@Output()
foldedChanged: EventEmitter<boolean>;
// Opened changed
@Output()
openedChanged: EventEmitter<boolean>;
// Private
private _folded: boolean;
private _fuseConfig: any;
private _wasActive: boolean;
private _wasFolded: boolean;
private _backdrop: HTMLElement | null = null;
private _player: AnimationPlayer;
private _unsubscribeAll: Subject<any>;
@HostBinding('class.animations-enabled')
private _animationsEnabled: boolean;
/**
* Constructor
*
* @param {AnimationBuilder} _animationBuilder
* @param {ChangeDetectorRef} _changeDetectorRef
* @param {ElementRef} _elementRef
* @param {FuseConfigService} _fuseConfigService
* @param {FuseMatchMediaService} _fuseMatchMediaService
* @param {FuseSidebarService} _fuseSidebarService
* @param {ObservableMedia} _observableMedia
* @param {Renderer2} _renderer
*/
constructor(
private _animationBuilder: AnimationBuilder,
private _changeDetectorRef: ChangeDetectorRef,
private _elementRef: ElementRef,
private _fuseConfigService: FuseConfigService,
private _fuseMatchMediaService: FuseMatchMediaService,
private _fuseSidebarService: FuseSidebarService,
private _observableMedia: ObservableMedia,
private _renderer: Renderer2
)
{
// Set the defaults
this.foldedAutoTriggerOnHover = true;
this.foldedWidth = 64;
this.foldedChanged = new EventEmitter();
this.openedChanged = new EventEmitter();
this.opened = false;
this.position = 'left';
this.invisibleOverlay = false;
// Set the private defaults
this._animationsEnabled = false;
this._folded = false;
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Folded
*
* @param {boolean} value
*/
@Input()
set folded(value: boolean)
{
// Only work if the sidebar is not closed
// Set the folded
this._folded = value;
// Return if the sidebar is closed
if ( !this.opened )
{
return;
}
// Set the folded
this._folded = value;
// Programmatically add/remove margin to the element
// that comes after or before based on the alignment
// Programmatically add/remove padding to the element
// that comes after or before based on the position
let sibling,
styleRule;
const styleValue = '64px';
const styleValue = this.foldedWidth + 'px';
// Get the sibling and set the style rule
if ( this.align === 'left' )
if ( this.position === 'left' )
{
sibling = this.elementRef.nativeElement.nextElementSibling;
styleRule = 'marginLeft';
sibling = this._elementRef.nativeElement.nextElementSibling;
styleRule = 'padding-left';
}
else
{
sibling = this.elementRef.nativeElement.previousElementSibling;
styleRule = 'marginRight';
sibling = this._elementRef.nativeElement.previousElementSibling;
styleRule = 'padding-right';
}
// If there is no sibling, return...
@@ -77,15 +163,36 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
// If folded...
if ( value )
{
// Set the style
this.renderer.setStyle(sibling, styleRule, styleValue);
// Fold the sidebar
this.fold();
// Set the folded width
this._renderer.setStyle(this._elementRef.nativeElement, 'width', styleValue);
this._renderer.setStyle(this._elementRef.nativeElement, 'min-width', styleValue);
this._renderer.setStyle(this._elementRef.nativeElement, 'max-width', styleValue);
// Set the style and class
this._renderer.setStyle(sibling, styleRule, styleValue, RendererStyleFlags2.Important + RendererStyleFlags2.DashCase);
this._renderer.addClass(this._elementRef.nativeElement, 'folded');
}
// If unfolded...
else
{
// Remove the style
this.renderer.removeStyle(sibling, styleRule);
// Unfold the sidebar
this.unfold();
// Remove the folded width
this._renderer.removeStyle(this._elementRef.nativeElement, 'width');
this._renderer.removeStyle(this._elementRef.nativeElement, 'min-width');
this._renderer.removeStyle(this._elementRef.nativeElement, 'max-width');
// Remove the style and class
this._renderer.removeStyle(sibling, styleRule);
this._renderer.removeClass(this._elementRef.nativeElement, 'folded');
}
// Emit the 'foldedChanged' event
this.foldedChanged.emit(this.folded);
}
get folded(): boolean
@@ -93,57 +200,36 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
return this._folded;
}
// Folded unfolded
@HostBinding('class.unfolded')
unfolded: boolean;
// Private
private _folded: boolean;
private _wasActive: boolean;
private _backdrop: HTMLElement | null = null;
private _player: AnimationPlayer;
private _onMediaChangeSubscription: Subscription;
/**
* Constructor
*
* @param {Renderer2} renderer
* @param {ElementRef} elementRef
* @param {AnimationBuilder} animationBuilder
* @param {ObservableMedia} observableMedia
* @param {FuseConfigService} fuseConfigService
* @param {FuseSidebarService} fuseSidebarService
* @param {FuseMatchMediaService} fuseMatchMediaService
*/
constructor(
private renderer: Renderer2,
private elementRef: ElementRef,
private animationBuilder: AnimationBuilder,
private observableMedia: ObservableMedia,
private fuseConfigService: FuseConfigService,
private fuseSidebarService: FuseSidebarService,
private fuseMatchMediaService: FuseMatchMediaService
)
{
// Set the defaults
this.opened = false;
this.folded = false;
this.align = 'left';
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Register the sidebar
this.fuseSidebarService.register(this.name, this);
// Subscribe to config changes
this._fuseConfigService.config
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config) => {
this._fuseConfig = config;
});
// Setup alignment
this._setupAlignment();
// Register the sidebar
this._fuseSidebarService.register(this.name, this);
// Setup visibility
this._setupVisibility();
// Setup position
this._setupPosition();
// Setup lockedOpen
this._setupLockedOpen();
// Setup folded
this._setupFolded();
}
/**
@@ -158,28 +244,47 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
}
// Unregister the sidebar
this.fuseSidebarService.unregister(this.name);
this._fuseSidebarService.unregister(this.name);
// Unsubscribe from the media watcher subscription
this._onMediaChangeSubscription.unsubscribe();
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Set the sidebar alignment
* Setup the visibility of the sidebar
*
* @private
*/
private _setupAlignment(): void
private _setupVisibility(): void
{
// Remove the existing box-shadow
this._renderer.setStyle(this._elementRef.nativeElement, 'box-shadow', 'none');
// Make the sidebar invisible
this._renderer.setStyle(this._elementRef.nativeElement, 'visibility', 'hidden');
}
/**
* Setup the sidebar position
*
* @private
*/
private _setupPosition(): void
{
// Add the correct class name to the sidebar
// element depending on the align attribute
if ( this.align === 'right' )
// element depending on the position attribute
if ( this.position === 'right' )
{
this.renderer.addClass(this.elementRef.nativeElement, 'right-aligned');
this._renderer.addClass(this._elementRef.nativeElement, 'right-positioned');
}
else
{
this.renderer.addClass(this.elementRef.nativeElement, 'left-aligned');
this._renderer.addClass(this._elementRef.nativeElement, 'left-positioned');
}
}
@@ -193,19 +298,26 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
// Return if the lockedOpen wasn't set
if ( !this.lockedOpen )
{
// Return
return;
}
// Set the wasActive for the first time
this._wasActive = false;
// Act on every media change
this._onMediaChangeSubscription =
// Set the wasFolded
this._wasFolded = this.folded;
this.fuseMatchMediaService.onMediaChange.subscribe(() => {
// Show the sidebar
this._showSidebar();
// Act on every media change
this._fuseMatchMediaService.onMediaChange
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(() => {
// Get the active status
const isActive = this.observableMedia.isActive(this.lockedOpen);
const isActive = this._observableMedia.isActive(this.lockedOpen);
// If the both status are the same, don't act
if ( this._wasActive === isActive )
@@ -219,18 +331,30 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
// Set the lockedOpen status
this.isLockedOpen = true;
// Show the sidebar
this._showSidebar();
// Force the the opened status to true
this.opened = true;
// Read the folded setting from the config
// and fold the sidebar if it's true
if ( this.fuseConfigService.config.layout.navigationFolded )
// Emit the 'openedChanged' event
this.openedChanged.emit(this.opened);
// If the sidebar was folded, forcefully fold it again
if ( this._wasFolded )
{
this.fold();
// Enable the animations
this._enableAnimations();
// Fold
this.folded = true;
// Mark for check
this._changeDetectorRef.markForCheck();
}
// Hide the backdrop if any exists
this.hideBackdrop();
this._hideBackdrop();
}
// De-Activate the lockedOpen
else
@@ -243,6 +367,12 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
// Force the the opened status to close
this.opened = false;
// Emit the 'openedChanged' event
this.openedChanged.emit(this.opened);
// Hide the sidebar
this._hideSidebar();
}
// Store the new active status
@@ -250,6 +380,209 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
});
}
/**
* Setup the initial folded status
*
* @private
*/
private _setupFolded(): void
{
// Return, if sidebar is not folded
if ( !this.folded )
{
return;
}
// Return if the sidebar is closed
if ( !this.opened )
{
return;
}
// Programmatically add/remove padding to the element
// that comes after or before based on the position
let sibling,
styleRule;
const styleValue = this.foldedWidth + 'px';
// Get the sibling and set the style rule
if ( this.position === 'left' )
{
sibling = this._elementRef.nativeElement.nextElementSibling;
styleRule = 'padding-left';
}
else
{
sibling = this._elementRef.nativeElement.previousElementSibling;
styleRule = 'padding-right';
}
// If there is no sibling, return...
if ( !sibling )
{
return;
}
// Fold the sidebar
this.fold();
// Set the folded width
this._renderer.setStyle(this._elementRef.nativeElement, 'width', styleValue);
this._renderer.setStyle(this._elementRef.nativeElement, 'min-width', styleValue);
this._renderer.setStyle(this._elementRef.nativeElement, 'max-width', styleValue);
// Set the style and class
this._renderer.setStyle(sibling, styleRule, styleValue, RendererStyleFlags2.Important + RendererStyleFlags2.DashCase);
this._renderer.addClass(this._elementRef.nativeElement, 'folded');
}
/**
* Show the backdrop
*
* @private
*/
private _showBackdrop(): void
{
// Create the backdrop element
this._backdrop = this._renderer.createElement('div');
// Add a class to the backdrop element
this._backdrop.classList.add('fuse-sidebar-overlay');
// Add a class depending on the invisibleOverlay option
if ( this.invisibleOverlay )
{
this._backdrop.classList.add('fuse-sidebar-overlay-invisible');
}
// Append the backdrop to the parent of the sidebar
this._renderer.appendChild(this._elementRef.nativeElement.parentElement, this._backdrop);
// Create the enter animation and attach it to the player
this._player =
this._animationBuilder
.build([
animate('300ms ease', style({opacity: 1}))
]).create(this._backdrop);
// Play the animation
this._player.play();
// Add an event listener to the overlay
this._backdrop.addEventListener('click', () => {
this.close();
}
);
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Hide the backdrop
*
* @private
*/
private _hideBackdrop(): void
{
if ( !this._backdrop )
{
return;
}
// Create the leave animation and attach it to the player
this._player =
this._animationBuilder
.build([
animate('300ms ease', style({opacity: 0}))
]).create(this._backdrop);
// Play the animation
this._player.play();
// Once the animation is done...
this._player.onDone(() => {
// If the backdrop still exists...
if ( this._backdrop )
{
// Remove the backdrop
this._backdrop.parentNode.removeChild(this._backdrop);
this._backdrop = null;
}
});
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Change some properties of the sidebar
* and make it visible
*
* @private
*/
private _showSidebar(): void
{
// Remove the box-shadow style
this._renderer.removeStyle(this._elementRef.nativeElement, 'box-shadow');
// Make the sidebar invisible
this._renderer.removeStyle(this._elementRef.nativeElement, 'visibility');
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Change some properties of the sidebar
* and make it invisible
*
* @private
*/
private _hideSidebar(delay = true): void
{
const delayAmount = delay ? 300 : 0;
// Add a delay so close animation can play
setTimeout(() => {
// Remove the box-shadow
this._renderer.setStyle(this._elementRef.nativeElement, 'box-shadow', 'none');
// Make the sidebar invisible
this._renderer.setStyle(this._elementRef.nativeElement, 'visibility', 'hidden');
}, delayAmount);
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Enable the animations
*
* @private
*/
private _enableAnimations(): void
{
// Return if animations already enabled
if ( this._animationsEnabled )
{
return;
}
// Enable the animations
this._animationsEnabled = true;
// Mark for check
this._changeDetectorRef.markForCheck();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open the sidebar
*/
@@ -260,11 +593,23 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
return;
}
// Enable the animations
this._enableAnimations();
// Show the sidebar
this._showSidebar();
// Show the backdrop
this.showBackdrop();
this._showBackdrop();
// Set the opened status
this.opened = true;
// Emit the 'openedChanged' event
this.openedChanged.emit(this.opened);
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
@@ -277,11 +622,23 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
return;
}
// Enable the animations
this._enableAnimations();
// Hide the backdrop
this.hideBackdrop();
this._hideBackdrop();
// Set the opened status
this.opened = false;
// Emit the 'openedChanged' event
this.openedChanged.emit(this.opened);
// Hide the sidebar
this._hideSidebar();
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
@@ -305,14 +662,13 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
@HostListener('mouseenter')
onMouseEnter(): void
{
// Only work if the sidebar is folded
if ( !this.folded )
// Only work if the auto trigger is enabled
if ( !this.foldedAutoTriggerOnHover )
{
return;
}
// Unfold the sidebar temporarily
this.unfolded = true;
this.unfoldTemporarily();
}
/**
@@ -321,14 +677,13 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
@HostListener('mouseleave')
onMouseLeave(): void
{
// Only work if the sidebar is folded
if ( !this.folded )
// Only work if the auto trigger is enabled
if ( !this.foldedAutoTriggerOnHover )
{
return;
}
// Fold the sidebar back
this.unfolded = false;
this.foldTemporarily();
}
/**
@@ -342,8 +697,14 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
return;
}
// Enable the animations
this._enableAnimations();
// Fold
this.folded = true;
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
@@ -357,8 +718,14 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
return;
}
// Enable the animations
this._enableAnimations();
// Unfold
this.folded = false;
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
@@ -377,66 +744,56 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
}
/**
* Show the backdrop
* Fold the temporarily unfolded sidebar back
*/
showBackdrop(): void
foldTemporarily(): void
{
// Create the backdrop element
this._backdrop = this.renderer.createElement('div');
// Add a class to the backdrop element
this._backdrop.classList.add('fuse-sidebar-overlay');
// Append the backdrop to the parent of the sidebar
this.renderer.appendChild(this.elementRef.nativeElement.parentElement, this._backdrop);
// Create the enter animation and attach it to the player
this._player =
this.animationBuilder
.build([
animate('300ms ease', style({opacity: 1}))
]).create(this._backdrop);
// Play the animation
this._player.play();
// Add an event listener to the overlay
this._backdrop.addEventListener('click', () => {
this.close();
}
);
}
/**
* Hide the backdrop
*/
hideBackdrop(): void
{
if ( !this._backdrop )
// Only work if the sidebar is folded
if ( !this.folded )
{
return;
}
// Create the leave animation and attach it to the player
this._player =
this.animationBuilder
.build([
animate('300ms ease', style({opacity: 0}))
]).create(this._backdrop);
// Enable the animations
this._enableAnimations();
// Play the animation
this._player.play();
// Fold the sidebar back
this.unfolded = false;
// Once the animation is done...
this._player.onDone(() => {
// Set the folded width
const styleValue = this.foldedWidth + 'px';
// If the backdrop still exists...
if ( this._backdrop )
{
// Remove the backdrop
this._backdrop.parentNode.removeChild(this._backdrop);
this._backdrop = null;
}
});
this._renderer.setStyle(this._elementRef.nativeElement, 'width', styleValue);
this._renderer.setStyle(this._elementRef.nativeElement, 'min-width', styleValue);
this._renderer.setStyle(this._elementRef.nativeElement, 'max-width', styleValue);
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Unfold the sidebar temporarily
*/
unfoldTemporarily(): void
{
// Only work if the sidebar is folded
if ( !this.folded )
{
return;
}
// Enable the animations
this._enableAnimations();
// Unfold the sidebar temporarily
this.unfolded = true;
// Remove the folded width
this._renderer.removeStyle(this._elementRef.nativeElement, 'width');
this._renderer.removeStyle(this._elementRef.nativeElement, 'min-width');
this._renderer.removeStyle(this._elementRef.nativeElement, 'max-width');
// Mark for check
this._changeDetectorRef.markForCheck();
}
}

View File

@@ -2,7 +2,9 @@ import { Injectable } from '@angular/core';
import { FuseSidebarComponent } from './sidebar.component';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class FuseSidebarService
{
// Private

View File

@@ -1,105 +1,495 @@
<button #openButton mat-icon-button class="open-button mat-primary-bg mat-elevation-z2" (click)="openBar()">
<mat-icon>settings</mat-icon>
</button>
<div class="theme-options-panel" fusePerfectScrollbar>
<div class="theme-options-panel-overlay" #overlay [fxHide]="barClosed" [@fadeInOut]="!barClosed"></div>
<div class="header">
<div #panel class="theme-options-panel mat-white-bg mat-elevation-z8">
<span class="title">Theme Options</span>
<button mat-icon-button class="close-button" (click)="closeBar()">
<mat-icon>close</mat-icon>
</button>
<button mat-icon-button class="close-button" (click)="toggleSidebarOpen('themeOptionsPanel')">
<mat-icon>close</mat-icon>
</button>
<div class="theme-options-panel-inner" fxLayout="column" fxLayoutAlign="start start">
</div>
<h3>Navigation:</h3>
<mat-radio-group [(ngModel)]="config.layout.navigation" (ngModelChange)="onSettingsChange()"
fxLayout="column" fxLayout.gt-xs="row wrap" fxLayoutAlign="start start">
<mat-radio-button class="mr-8 mb-8" value="top">Top</mat-radio-button>
<mat-radio-button class="mr-8 mb-8" value="left">Left</mat-radio-button>
<mat-radio-button class="mr-8 mb-8" value="right">Right</mat-radio-button>
<mat-radio-button class="mr-8 mb-8" value="none">None</mat-radio-button>
</mat-radio-group>
<form [formGroup]="form">
<h3>Navigation Fold (for vertical navigation):</h3>
<mat-slide-toggle [(ngModel)]="config.layout.navigationFolded"
(change)="onSettingsChange()">
Folded
</mat-slide-toggle>
<!-- LAYOUT STYLES -->
<div class="group" formGroupName="layout">
<h3 class="mt-24">Toolbar:</h3>
<mat-radio-group [(ngModel)]="config.layout.toolbar" (ngModelChange)="onSettingsChange()"
fxLayout="column" fxLayout.gt-xs="row wrap" fxLayoutAlign="start start">
<mat-radio-button class="mr-8 mb-8" value="below">Below</mat-radio-button>
<mat-radio-button class="mr-8 mb-8" value="above">Above</mat-radio-button>
<mat-radio-button class="mr-8 mb-8" value="none">None</mat-radio-button>
</mat-radio-group>
<h2>Layout Styles</h2>
<h3 class="mt-24">Footer:</h3>
<mat-radio-group [(ngModel)]="config.layout.footer" (ngModelChange)="onSettingsChange()"
fxLayout="column" fxLayout.gt-xs="row wrap" fxLayoutAlign="start start">
<mat-radio-button class="mr-8 mb-8" value="below">Below</mat-radio-button>
<mat-radio-button class="mr-8 mb-8" value="above">Above</mat-radio-button>
<mat-radio-button class="mr-8 mb-8" value="none">None</mat-radio-button>
</mat-radio-group>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="style">
<h3 class="mt-24">Layout Mode:</h3>
<mat-radio-group [(ngModel)]="config.layout.mode" (ngModelChange)="onSettingsChange()"
fxLayout="column" fxLayout.gt-xs="row wrap" fxLayoutAlign="start start">
<mat-radio-button class="mr-8 mb-8" value="boxed">Boxed</mat-radio-button>
<mat-radio-button class="mr-8 mb-8" value="fullwidth">Fullwidth</mat-radio-button>
</mat-radio-group>
<mat-radio-button class="mb-12" value="vertical-layout-1">
Vertical Layout #1
</mat-radio-button>
<mat-divider></mat-divider>
<mat-radio-button class="mb-12" value="vertical-layout-2">
Vertical Layout #2
</mat-radio-button>
<h3>Colors:</h3>
<div class="colors">
<mat-radio-button class="mb-12" value="vertical-layout-3">
Vertical Layout #3
</mat-radio-button>
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<h4 class="mr-8">Toolbar Color</h4>
<fuse-material-color-picker [(selectedClass)]="config.colorClasses.toolbar"
(onValueChange)="onSettingsChange()"></fuse-material-color-picker>
</div>
<mat-radio-button class="mb-12" value="horizontal-layout-1">
Horizontal Layout #1
</mat-radio-button>
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<h4 class="mr-8">Navigation Bar Color</h4>
<fuse-material-color-picker [(selectedClass)]="config.colorClasses.navbar"
(onValueChange)="onSettingsChange()"></fuse-material-color-picker>
</div>
</mat-radio-group>
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
<h4 class="mr-8">Footer Color</h4>
<fuse-material-color-picker [(selectedClass)]="config.colorClasses.footer"
(onValueChange)="onSettingsChange()"></fuse-material-color-picker>
</div>
<!-- DIFFERENT FORMS BASED ON LAYOUT STYLES -->
<ng-container [ngSwitch]="fuseConfig.layout.style">
<!-- VERTICAL LAYOUT #1 -->
<ng-container *ngSwitchCase="'vertical-layout-1'">
<!-- LAYOUT WIDTH -->
<div class="group mt-32">
<h2>Layout Width</h2>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="width">
<mat-radio-button class="mb-12" value="fullwidth">Fullwidth</mat-radio-button>
<mat-radio-button class="mb-12" value="boxed">Boxed</mat-radio-button>
</mat-radio-group>
</div>
<!-- NAVBAR -->
<div class="group" formGroupName="navbar">
<h2>Navbar</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<mat-slide-toggle class="mt-24" formControlName="folded">
Folded
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-16" value="left">Left</mat-radio-button>
<mat-radio-button class="mb-16" value="right">Right</mat-radio-button>
</mat-radio-group>
<h3 class="mt-8">Variant:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="variant">
<mat-radio-button class="mb-16" value="vertical-style-1">Style 1</mat-radio-button>
<mat-radio-button class="mb-16" value="vertical-style-2">Style 2</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.navbar.background">
</fuse-material-color-picker>
</div>
<!-- TOOLBAR -->
<div class="group" formGroupName="toolbar">
<h2>Toolbar</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="above">Above</mat-radio-button>
<mat-radio-button class="mb-12" value="below-static">Below Static</mat-radio-button>
<mat-radio-button class="mb-12" value="below-fixed">Below Fixed</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.toolbar.background">
</fuse-material-color-picker>
</div>
<!-- FOOTER -->
<div class="group" formGroupName="footer">
<h2>Footer</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="above">Above</mat-radio-button>
<mat-radio-button class="mb-12" value="below-static">Below Static</mat-radio-button>
<mat-radio-button class="mb-12" value="below-fixed">Below Fixed</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.footer.background">
</fuse-material-color-picker>
</div>
<!-- SIDE PANEL -->
<div class="group" formGroupName="sidepanel">
<h2>Side Panel</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="left">Left</mat-radio-button>
<mat-radio-button class="mb-12" value="right">Right</mat-radio-button>
</mat-radio-group>
</div>
</ng-container>
<!-- VERTICAL LAYOUT #2 -->
<ng-container *ngSwitchCase="'vertical-layout-2'">
<!-- LAYOUT WIDTH -->
<div class="group mt-32">
<h2>Layout Width</h2>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="width">
<mat-radio-button class="mb-12" value="fullwidth">Fullwidth</mat-radio-button>
<mat-radio-button class="mb-12" value="boxed">Boxed</mat-radio-button>
</mat-radio-group>
</div>
<!-- NAVBAR -->
<div class="group" formGroupName="navbar">
<h2>Navbar</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<mat-slide-toggle class="mt-24" formControlName="folded">
Folded
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-16" value="left">Left</mat-radio-button>
<mat-radio-button class="mb-16" value="right">Right</mat-radio-button>
</mat-radio-group>
<h3 class="mt-8">Variant:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="variant">
<mat-radio-button class="mb-16" value="vertical-style-1">Style 1</mat-radio-button>
<mat-radio-button class="mb-16" value="vertical-style-2">Style 2</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.navbar.background">
</fuse-material-color-picker>
</div>
<!-- TOOLBAR -->
<div class="group" formGroupName="toolbar">
<h2>Toolbar</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
<mat-radio-button class="mb-12" value="below">Below</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.toolbar.background">
</fuse-material-color-picker>
</div>
<!-- FOOTER -->
<div class="group" formGroupName="footer">
<h2>Footer</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
<mat-radio-button class="mb-12" value="below">Below</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.footer.background">
</fuse-material-color-picker>
</div>
<!-- SIDE PANEL -->
<div class="group" formGroupName="sidepanel">
<h2>Side Panel</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="left">Left</mat-radio-button>
<mat-radio-button class="mb-12" value="right">Right</mat-radio-button>
</mat-radio-group>
</div>
</ng-container>
<!-- VERTICAL LAYOUT #3 -->
<ng-container *ngSwitchCase="'vertical-layout-3'">
<!-- LAYOUT WIDTH -->
<div class="group mt-32">
<h2>Layout Width</h2>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="width">
<mat-radio-button class="mb-12" value="fullwidth">Fullwidth</mat-radio-button>
<mat-radio-button class="mb-12" value="boxed">Boxed</mat-radio-button>
</mat-radio-group>
</div>
<!-- NAVBAR -->
<div class="group" formGroupName="navbar">
<h2>Navbar</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<mat-slide-toggle class="mt-24" formControlName="folded">
Folded
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-16" value="left">Left</mat-radio-button>
<mat-radio-button class="mb-16" value="right">Right</mat-radio-button>
</mat-radio-group>
<h3 class="mt-8">Variant:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="variant">
<mat-radio-button class="mb-16" value="vertical-style-1">Style 1</mat-radio-button>
<mat-radio-button class="mb-16" value="vertical-style-2">Style 2</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.navbar.background">
</fuse-material-color-picker>
</div>
<!-- TOOLBAR -->
<div class="group" formGroupName="toolbar">
<h2>Toolbar</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.toolbar.background">
</fuse-material-color-picker>
</div>
<!-- FOOTER -->
<div class="group" formGroupName="footer">
<h2>Footer</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.footer.background">
</fuse-material-color-picker>
</div>
<!-- SIDE PANEL -->
<div class="group" formGroupName="sidepanel">
<h2>Side Panel</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="left">Left</mat-radio-button>
<mat-radio-button class="mb-12" value="right">Right</mat-radio-button>
</mat-radio-group>
</div>
</ng-container>
<!-- HORIZONTAL LAYOUT #1 -->
<ng-container *ngSwitchCase="'horizontal-layout-1'">
<!-- LAYOUT WIDTH -->
<div class="group mt-32">
<h2>Layout Width</h2>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="width">
<mat-radio-button class="mb-12" value="fullwidth">Fullwidth</mat-radio-button>
<mat-radio-button class="mb-12" value="boxed">Boxed</mat-radio-button>
</mat-radio-group>
</div>
<!-- NAVBAR -->
<div class="group" formGroupName="navbar">
<h2>Navbar</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-16" value="top">Top</mat-radio-button>
</mat-radio-group>
<h3 class="mt-8">Variant (Vertical):</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="variant">
<mat-radio-button class="mb-16" value="vertical-style-1">Style 1</mat-radio-button>
<mat-radio-button class="mb-16" value="vertical-style-2">Style 2</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.navbar.background">
</fuse-material-color-picker>
</div>
<!-- TOOLBAR -->
<div class="group" formGroupName="toolbar">
<h2>Toolbar</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="above">Above</mat-radio-button>
<mat-radio-button class="mb-12" value="below">Below</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.toolbar.background">
</fuse-material-color-picker>
</div>
<!-- FOOTER -->
<div class="group" formGroupName="footer">
<h2>Footer</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
</mat-radio-group>
<h3 class="mt-24 mb-8">Color:</h3>
<fuse-material-color-picker class="mb-16"
[(selectedClass)]="fuseConfig.layout.footer.background">
</fuse-material-color-picker>
</div>
<!-- SIDE PANEL -->
<div class="group" formGroupName="sidepanel">
<h2>Side Panel</h2>
<mat-slide-toggle formControlName="hidden">
Hide
</mat-slide-toggle>
<h3 class="mt-24">Position:</h3>
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
<mat-radio-button class="mb-12" value="left">Left</mat-radio-button>
<mat-radio-button class="mb-12" value="right">Right</mat-radio-button>
</mat-radio-group>
</div>
</ng-container>
</ng-container>
</div>
<mat-divider></mat-divider>
<!-- CUSTOM SCROLLBARS -->
<div class="group">
<h3>Router Animation:</h3>
<mat-form-field class="w-100-p">
<mat-select class="p-0" [(ngModel)]="config.routerAnimation">
<mat-option value="none">
None
</mat-option>
<mat-option value="slideUp">
Slide up
</mat-option>
<mat-option value="slideDown">
Slide down
</mat-option>
<mat-option value="slideRight">
Slide right
</mat-option>
<mat-option value="slideLeft">
Slide left
</mat-option>
<mat-option value="fadeIn">
Fade in
</mat-option>
</mat-select>
</mat-form-field>
</div>
<h2>Custom scrollbars</h2>
<mat-slide-toggle class="mb-12" formControlName="customScrollbars">
Enable custom scrollbars
</mat-slide-toggle>
</div>
</form>
</div>

View File

@@ -10,106 +10,69 @@
}
:host {
position: fixed;
display: block;
right: 0;
top: 160px;
z-index: 998;
&.bar-closed .theme-options-panel {
display: none;
}
display: flex;
overflow: hidden;
.theme-options-panel {
position: absolute;
right: 0;
top: 0;
width: 360px;
transform: translate3d(100%, 0, 0);
z-index: 999;
max-height: calc(100vh - 200px);
padding: 24px;
display: flex;
flex-direction: column;
flex: 1 0 auto;
padding: 40px 24px 24px 24px;
overflow: auto;
@include media-breakpoint-down('xs') {
top: -120px;
max-height: calc(100vh - 100px);
width: 90vw;
.header {
display: flex;
flex: 0 1 auto;
margin-bottom: 32px;
align-items: center;
justify-content: space-between;
.title {
font-size: 20px;
font-weight: 500;
padding-left: 4px;
}
}
.close-button {
position: absolute;
top: 8px;
right: 8px;
}
form {
display: flex;
flex: 1 1 auto;
flex-direction: column;
h3 {
font-size: 14px;
font-weight: 500;
color: rgba(0, 0, 0, 0.54);
}
.group {
display: flex;
flex: 1 0 auto;
flex-direction: column;
position: relative;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 2px;
padding: 28px 16px 8px 16px;
margin: 16px 0;
.mat-divider {
display: block !important;
width: 100%;
margin: 24px 0 16px 0;
}
h2 {
position: absolute;
top: -11px;
left: 8px;
margin: 0;
padding: 0 8px;
font-size: 16px;
font-weight: 500;
background: white;
color: rgba(0, 0, 0, 0.54);
}
.colors {
display: block !important;
width: 100%;
h3 {
font-size: 14px;
font-weight: 500;
color: rgba(0, 0, 0, 0.54);
margin: 24px 0 16px 0;
padding: 0;
&:first-of-type {
margin-top: 0;
}
}
}
}
}
.theme-options-panel-overlay {
position: fixed;
display: block;
background: rgba(0, 0, 0, 0);
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 998;
@include media-breakpoint-down('sm') {
background: rgba(0, 0, 0, 0.37);
}
&.hidden {
display: none;
}
}
.mat-list .mat-list-item {
font-size: 15px;
}
.mat-divider {
margin: 16px;
}
.open-button {
position: absolute;
top: 0;
left: -48px;
width: 48px;
height: 48px;
line-height: 48px;
text-align: center;
cursor: pointer;
border-radius: 0;
margin: 0;
pointer-events: auto;
opacity: .75;
z-index: 998;
mat-icon {
animation: rotating 3s linear infinite;
}
&:hover {
opacity: 1;
}
}
}

View File

@@ -1,10 +1,12 @@
import { Component, ElementRef, HostBinding, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core';
import { style, animate, AnimationBuilder, AnimationPlayer } from '@angular/animations';
import { Subscription } from 'rxjs';
import { Component, HostBinding, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations';
import { FuseConfigService } from '@fuse/services/config.service';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
@Component({
selector : 'fuse-theme-options',
@@ -14,50 +16,118 @@ import { FuseNavigationService } from '@fuse/components/navigation/navigation.se
})
export class FuseThemeOptionsComponent implements OnInit, OnDestroy
{
@Input() navigation;
@ViewChild('openButton') openButton;
@ViewChild('panel') panel;
@ViewChild('overlay') overlay: ElementRef;
fuseConfig: any;
form: FormGroup;
public player: AnimationPlayer;
config: any;
@HostBinding('class.bar-closed')
barClosed: boolean;
onConfigChanged: Subscription;
@HostBinding('class.bar-closed') barClosed: boolean;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {FormBuilder} _formBuilder
* @param {FuseConfigService} _fuseConfigService
* @param {FuseNavigationService} _fuseNavigationService
* @param {FuseSidebarService} _fuseSidebarService
* @param {Renderer2} _renderer
*/
constructor(
private animationBuilder: AnimationBuilder,
private fuseConfig: FuseConfigService,
private navigationService: FuseNavigationService,
private renderer: Renderer2
private _formBuilder: FormBuilder,
private _fuseConfigService: FuseConfigService,
private _fuseNavigationService: FuseNavigationService,
private _fuseSidebarService: FuseSidebarService,
private _renderer: Renderer2
)
{
// Set the defaults
this.barClosed = true;
this.onConfigChanged =
this.fuseConfig.onConfigChanged
.subscribe(
(newConfig) => {
this.config = newConfig;
}
);
// Set the private defaults
this._unsubscribeAll = new Subject();
}
ngOnInit()
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
this.renderer.listen(this.overlay.nativeElement, 'click', () => {
this.closeBar();
// Build the config form
// noinspection TypeScriptValidateTypes
this.form = this._formBuilder.group({
layout : this._formBuilder.group({
style : new FormControl(),
width : new FormControl(),
navbar : this._formBuilder.group({
background: new FormControl(),
folded : new FormControl(),
hidden : new FormControl(),
position : new FormControl(),
variant : new FormControl()
}),
toolbar : this._formBuilder.group({
background: new FormControl(),
hidden : new FormControl(),
position : new FormControl()
}),
footer : this._formBuilder.group({
background: new FormControl(),
hidden : new FormControl(),
position : new FormControl()
}),
sidepanel: this._formBuilder.group({
hidden: new FormControl(),
position : new FormControl()
})
}),
customScrollbars: new FormControl()
});
// Get the nav model and add customize nav item
// that opens the bar programmatically
const nav: any = this.navigation;
// Subscribe to the config changes
this._fuseConfigService.config
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config) => {
nav.push({
// Update the stored config
this.fuseConfig = config;
// Set the config form values without emitting an event
// so that we don't end up with an infinite loop
this.form.setValue(config, {emitEvent: false});
});
// Subscribe to the specific form value changes (layout.style)
this.form.get('layout.style').valueChanges
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((value) => {
// Reset the form values based on the
// selected layout style
this._resetFormValues(value);
});
// Subscribe to the form value changes
this.form.valueChanges
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config) => {
// Update the config
this._fuseConfigService.config = config;
});
// Add customize nav item that opens the bar programmatically
const customFunctionNavItem = {
'id' : 'custom-function',
'title' : 'Custom Function',
'type' : 'group',
'icon' : 'settings',
'children': [
{
'id' : 'customize',
@@ -65,50 +135,173 @@ export class FuseThemeOptionsComponent implements OnInit, OnDestroy
'type' : 'item',
'icon' : 'settings',
'function': () => {
this.openBar();
this.toggleSidebarOpen('themeOptionsPanel');
}
}
]
});
};
this._fuseNavigationService.addNavigationItem(customFunctionNavItem, 'end');
}
ngOnDestroy()
/**
* On destroy
*/
ngOnDestroy(): void
{
this.onConfigChanged.unsubscribe();
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
// Remove the custom function menu
this._fuseNavigationService.removeNavigationItem('custom-function');
}
onSettingsChange()
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Reset the form values based on the
* selected layout style
*
* @param value
* @private
*/
private _resetFormValues(value): void
{
this.fuseConfig.setConfig(this.config);
switch ( value )
{
// Vertical Layout #1
case 'vertical-layout-1':
{
this.form.patchValue({
layout: {
width : 'fullwidth',
navbar : {
background: 'mat-fuse-dark-700-bg',
folded : false,
hidden : false,
position : 'left',
variant : 'vertical-style-1'
},
toolbar: {
background: 'mat-white-500-bg',
hidden : false,
position : 'below-static'
},
footer : {
background: 'mat-fuse-dark-900-bg',
hidden : false,
position : 'below-static'
}
}
});
break;
}
// Vertical Layout #2
case 'vertical-layout-2':
{
this.form.patchValue({
layout: {
width : 'fullwidth',
navbar : {
background: 'mat-fuse-dark-700-bg',
folded : false,
hidden : false,
position : 'left',
variant : 'vertical-style-1'
},
toolbar: {
background: 'mat-white-500-bg',
hidden : false,
position : 'below'
},
footer : {
background: 'mat-fuse-dark-900-bg',
hidden : false,
position : 'below'
}
}
});
break;
}
// Vertical Layout #3
case 'vertical-layout-3':
{
this.form.patchValue({
layout: {
width : 'fullwidth',
navbar : {
background: 'mat-fuse-dark-700-bg',
folded : false,
hidden : false,
position : 'left',
layout : 'vertical-style-1'
},
toolbar: {
background: 'mat-white-500-bg',
hidden : false,
position : 'above-static'
},
footer : {
background: 'mat-fuse-dark-900-bg',
hidden : false,
position : 'above-static'
}
}
});
break;
}
// Horizontal Layout #1
case 'horizontal-layout-1':
{
this.form.patchValue({
layout: {
width : 'fullwidth',
navbar : {
background: 'mat-fuse-dark-700-bg',
folded : false,
hidden : false,
position : 'top',
variant : 'vertical-style-1'
},
toolbar: {
background: 'mat-white-500-bg',
hidden : false,
position : 'above'
},
footer : {
background: 'mat-fuse-dark-900-bg',
hidden : false,
position : 'above-fixed'
}
}
});
break;
}
}
}
closeBar()
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle sidebar open
*
* @param key
*/
toggleSidebarOpen(key): void
{
this.player =
this.animationBuilder
.build([
style({transform: 'translate3d(0,0,0)'}),
animate('400ms ease', style({transform: 'translate3d(100%,0,0)'}))
]).create(this.panel.nativeElement);
this.player.play();
this.player.onDone(() => {
this.barClosed = true;
});
this._fuseSidebarService.getSidebar(key).toggleOpen();
}
openBar()
{
this.barClosed = false;
this.player =
this.animationBuilder
.build([
style({transform: 'translate3d(100%,0,0)'}),
animate('400ms ease', style({transform: 'translate3d(0,0,0)'}))
]).create(this.panel.nativeElement);
this.player.play();
}
}

View File

@@ -1,10 +1,13 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule, MatDividerModule, MatFormFieldModule, MatIconModule, MatOptionModule, MatRadioModule, MatSelectModule, MatSlideToggleModule } from '@angular/material';
import { FuseDirectivesModule } from '@fuse/directives/directives';
import { FuseSidebarModule } from '@fuse/components/sidebar/sidebar.module';
import { FuseMaterialColorPickerModule } from '@fuse/components/material-color-picker/material-color-picker.module';
import { FuseThemeOptionsComponent } from '@fuse/components/theme-options/theme-options.component';
@NgModule({
@@ -14,6 +17,7 @@ import { FuseThemeOptionsComponent } from '@fuse/components/theme-options/theme-
imports : [
CommonModule,
FormsModule,
ReactiveFormsModule,
FlexLayoutModule,
@@ -26,7 +30,9 @@ import { FuseThemeOptionsComponent } from '@fuse/components/theme-options/theme-
MatSelectModule,
MatSlideToggleModule,
FuseMaterialColorPickerModule
FuseDirectivesModule,
FuseMaterialColorPickerModule,
FuseSidebarModule
],
exports : [
FuseThemeOptionsComponent

View File

@@ -5,7 +5,14 @@ import { Directive, ElementRef } from '@angular/core';
})
export class FuseWidgetToggleDirective
{
constructor(public el: ElementRef)
/**
* Constructor
*
* @param {ElementRef} elementRef
*/
constructor(
public elementRef: ElementRef
)
{
}
}

View File

@@ -10,19 +10,38 @@ import { FuseWidgetToggleDirective } from './widget-toggle.directive';
export class FuseWidgetComponent implements AfterContentInit
{
@HostBinding('class.flipped') flipped = false;
@ContentChildren(FuseWidgetToggleDirective, {descendants: true}) toggleButtons: QueryList<FuseWidgetToggleDirective>;
@HostBinding('class.flipped')
flipped = false;
constructor(private el: ElementRef, private renderer: Renderer2)
@ContentChildren(FuseWidgetToggleDirective, {descendants: true})
toggleButtons: QueryList<FuseWidgetToggleDirective>;
/**
* Constructor
*
* @param {ElementRef} _elementRef
* @param {Renderer2} _renderer
*/
constructor(
private _elementRef: ElementRef,
private _renderer: Renderer2
)
{
}
ngAfterContentInit()
{
setTimeout(() => {
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* After content init
*/
ngAfterContentInit(): void
{
// Listen for the flip button click
setTimeout(() => {
this.toggleButtons.forEach(flipButton => {
this.renderer.listen(flipButton.el.nativeElement, 'click', (event) => {
this._renderer.listen(flipButton.elementRef.nativeElement, 'click', (event) => {
event.preventDefault();
event.stopPropagation();
this.toggle();
@@ -31,7 +50,14 @@ export class FuseWidgetComponent implements AfterContentInit
});
}
toggle()
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle the flipped status
*/
toggle(): void
{
this.flipped = !this.flipped;
}

View File

@@ -1,12 +1,14 @@
import { NgModule } from '@angular/core';
import { FuseIfOnDomDirective } from '@fuse/directives/fuse-if-on-dom/fuse-if-on-dom.directive';
import { FuseInnerScrollDirective } from '@fuse/directives/fuse-inner-scroll/fuse-inner-scroll.directive';
import { FusePerfectScrollbarDirective } from '@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive';
import { FuseMatSidenavHelperDirective, FuseMatSidenavTogglerDirective } from '@fuse/directives/fuse-mat-sidenav/fuse-mat-sidenav.directive';
@NgModule({
declarations: [
FuseIfOnDomDirective,
FuseInnerScrollDirective,
FuseMatSidenavHelperDirective,
FuseMatSidenavTogglerDirective,
FusePerfectScrollbarDirective
@@ -14,6 +16,7 @@ import { FuseMatSidenavHelperDirective, FuseMatSidenavTogglerDirective } from '@
imports : [],
exports : [
FuseIfOnDomDirective,
FuseInnerScrollDirective,
FuseMatSidenavHelperDirective,
FuseMatSidenavTogglerDirective,
FusePerfectScrollbarDirective

View File

@@ -5,28 +5,44 @@ import { AfterContentChecked, Directive, ElementRef, TemplateRef, ViewContainerR
})
export class FuseIfOnDomDirective implements AfterContentChecked
{
isCreated = false;
isCreated: boolean;
/**
* Constructor
*
* @param {ElementRef} _elementRef
* @param {TemplateRef<any>} _templateRef
* @param {ViewContainerRef} _viewContainerRef
*/
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private element: ElementRef
private _elementRef: ElementRef,
private _templateRef: TemplateRef<any>,
private _viewContainerRef: ViewContainerRef
)
{
// Set the defaults
this.isCreated = false;
}
ngAfterContentChecked()
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* After content checked
*/
ngAfterContentChecked(): void
{
if ( document.body.contains(this.element.nativeElement) && !this.isCreated )
if ( document.body.contains(this._elementRef.nativeElement) && !this.isCreated )
{
setTimeout(() => {
this.viewContainer.createEmbeddedView(this.templateRef);
this._viewContainerRef.createEmbeddedView(this._templateRef);
}, 300);
this.isCreated = true;
}
else if ( this.isCreated && !document.body.contains(this.element.nativeElement) )
else if ( this.isCreated && !document.body.contains(this._elementRef.nativeElement) )
{
this.viewContainer.clear();
this._viewContainerRef.clear();
this.isCreated = false;
}
}

View File

@@ -0,0 +1,115 @@
import { Directive, ElementRef, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMatchMediaService } from '@fuse/services/match-media.service';
@Directive({
selector: '.inner-scroll'
})
export class FuseInnerScrollDirective implements OnInit, OnDestroy
{
// Private
private _parent: any;
private _grandParent: any;
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {ElementRef} _elementRef
* @param {FuseMatchMediaService} _fuseMediaMatchService
* @param {Renderer2} _renderer
*/
constructor(
private _elementRef: ElementRef,
private _fuseMediaMatchService: FuseMatchMediaService,
private _renderer: Renderer2
)
{
// Set the private defaults
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the parent
this._parent = this._renderer.parentNode(this._elementRef.nativeElement);
// Return, if there is no parent
if ( !this._parent )
{
return;
}
// Get the grand parent
this._grandParent = this._renderer.parentNode(this._parent);
// Register to the media query changes
this._fuseMediaMatchService.onMediaChange
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((alias) => {
if ( alias === 'xs' )
{
this._removeClass();
}
else
{
this._addClass();
}
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Return, if there is no parent
if ( !this._parent )
{
return;
}
// Remove the class
this._removeClass();
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Add the class name
*
* @private
*/
private _addClass(): void
{
// Add the inner-scroll class
this._renderer.addClass(this._grandParent, 'inner-scroll');
}
/**
* Remove the class name
* @private
*/
private _removeClass(): void
{
// Remove the inner-scroll class
this._renderer.removeClass(this._grandParent, 'inner-scroll');
}
}

View File

@@ -1,7 +1,8 @@
import { Directive, Input, OnInit, HostListener, OnDestroy, HostBinding } from '@angular/core';
import { MatSidenav } from '@angular/material';
import { ObservableMedia } from '@angular/flex-layout';
import { Subscription } from 'rxjs';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMatchMediaService } from '@fuse/services/match-media.service';
import { FuseMatSidenavHelperService } from '@fuse/directives/fuse-mat-sidenav/fuse-mat-sidenav.service';
@@ -11,56 +12,91 @@ import { FuseMatSidenavHelperService } from '@fuse/directives/fuse-mat-sidenav/f
})
export class FuseMatSidenavHelperDirective implements OnInit, OnDestroy
{
matchMediaSubscription: Subscription;
@HostBinding('class.mat-is-locked-open') isLockedOpen = true;
@Input('fuseMatSidenavHelper') id: string;
@Input('mat-is-locked-open') matIsLockedOpenBreakpoint: string;
@HostBinding('class.mat-is-locked-open')
isLockedOpen: boolean;
@Input('fuseMatSidenavHelper')
id: string;
@Input('mat-is-locked-open')
matIsLockedOpenBreakpoint: string;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {FuseMatchMediaService} _fuseMatchMediaService
* @param {FuseMatSidenavHelperService} _fuseMatSidenavHelperService
* @param {MatSidenav} _matSidenav
* @param {ObservableMedia} _observableMedia
*/
constructor(
private fuseMatSidenavService: FuseMatSidenavHelperService,
private fuseMatchMedia: FuseMatchMediaService,
private observableMedia: ObservableMedia,
private matSidenav: MatSidenav
private _fuseMatchMediaService: FuseMatchMediaService,
private _fuseMatSidenavHelperService: FuseMatSidenavHelperService,
private _matSidenav: MatSidenav,
private _observableMedia: ObservableMedia
)
{
// Set the defaults
this.isLockedOpen = true;
// Set the private defaults
this._unsubscribeAll = new Subject();
}
ngOnInit()
{
this.fuseMatSidenavService.setSidenav(this.id, this.matSidenav);
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
if ( this.observableMedia.isActive(this.matIsLockedOpenBreakpoint) )
/**
* On init
*/
ngOnInit(): void
{
// Register the sidenav to the service
this._fuseMatSidenavHelperService.setSidenav(this.id, this._matSidenav);
if ( this._observableMedia.isActive(this.matIsLockedOpenBreakpoint) )
{
this.isLockedOpen = true;
this.matSidenav.mode = 'side';
this.matSidenav.toggle(true);
this._matSidenav.mode = 'side';
this._matSidenav.toggle(true);
}
else
{
this.isLockedOpen = false;
this.matSidenav.mode = 'over';
this.matSidenav.toggle(false);
this._matSidenav.mode = 'over';
this._matSidenav.toggle(false);
}
this.matchMediaSubscription = this.fuseMatchMedia.onMediaChange.subscribe(() => {
if ( this.observableMedia.isActive(this.matIsLockedOpenBreakpoint) )
{
this.isLockedOpen = true;
this.matSidenav.mode = 'side';
this.matSidenav.toggle(true);
}
else
{
this.isLockedOpen = false;
this.matSidenav.mode = 'over';
this.matSidenav.toggle(false);
}
});
this._fuseMatchMediaService.onMediaChange
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(() => {
if ( this._observableMedia.isActive(this.matIsLockedOpenBreakpoint) )
{
this.isLockedOpen = true;
this._matSidenav.mode = 'side';
this._matSidenav.toggle(true);
}
else
{
this.isLockedOpen = false;
this._matSidenav.mode = 'over';
this._matSidenav.toggle(false);
}
});
}
ngOnDestroy()
/**
* On destroy
*/
ngOnDestroy(): void
{
this.matchMediaSubscription.unsubscribe();
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
}
@@ -69,15 +105,29 @@ export class FuseMatSidenavHelperDirective implements OnInit, OnDestroy
})
export class FuseMatSidenavTogglerDirective
{
@Input('fuseMatSidenavToggler') id;
@Input('fuseMatSidenavToggler')
id;
constructor(private fuseMatSidenavService: FuseMatSidenavHelperService)
/**
* Constructor
*
* @param {FuseMatSidenavHelperService} _fuseMatSidenavHelperService
*/
constructor(
private _fuseMatSidenavHelperService: FuseMatSidenavHelperService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* On click
*/
@HostListener('click')
onClick()
{
this.fuseMatSidenavService.getSidenav(this.id).toggle();
this._fuseMatSidenavHelperService.getSidenav(this.id).toggle();
}
}

View File

@@ -1,22 +1,43 @@
import { Injectable } from '@angular/core';
import { MatSidenav } from '@angular/material';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class FuseMatSidenavHelperService
{
sidenavInstances: MatSidenav[];
/**
* Constructor
*/
constructor()
{
this.sidenavInstances = [];
}
setSidenav(id, instance)
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Set sidenav
*
* @param id
* @param instance
*/
setSidenav(id, instance): void
{
this.sidenavInstances[id] = instance;
}
getSidenav(id)
/**
* Get sidenav
*
* @param id
* @returns {any}
*/
getSidenav(id): any
{
return this.sidenavInstances[id];
}

View File

@@ -1,72 +1,247 @@
import { AfterViewInit, Directive, ElementRef, HostListener, OnDestroy, OnInit } from '@angular/core';
import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnDestroy } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { Platform } from '@angular/cdk/platform';
import { Subscription } from 'rxjs';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import PerfectScrollbar from 'perfect-scrollbar';
import * as _ from 'lodash';
import { FuseConfigService } from '@fuse/services/config.service';
@Directive({
selector: '[fusePerfectScrollbar]'
})
export class FusePerfectScrollbarDirective implements OnInit, AfterViewInit, OnDestroy
export class FusePerfectScrollbarDirective implements AfterViewInit, OnDestroy
{
onConfigChanged: Subscription;
isDisableCustomScrollbars = false;
isMobile = false;
isInitialized = true;
isInitialized: boolean;
isMobile: boolean;
ps: PerfectScrollbar;
// Private
private _enabled: boolean | '';
private _debouncedUpdate: any;
private _options: any;
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {ElementRef} elementRef
* @param {FuseConfigService} _fuseConfigService
* @param {Platform} _platform
* @param {Router} _router
*/
constructor(
public element: ElementRef,
private fuseConfig: FuseConfigService,
private platform: Platform
public elementRef: ElementRef,
private _fuseConfigService: FuseConfigService,
private _platform: Platform,
private _router: Router
)
{
// Set the defaults
this.isInitialized = false;
this.isMobile = false;
// Set the private defaults
this._enabled = false;
this._debouncedUpdate = _.debounce(this.update, 150);
this._options = {
updateOnRouteChange: false
};
this._unsubscribeAll = new Subject();
}
ngOnInit()
{
this.onConfigChanged =
this.fuseConfig.onConfigChanged.subscribe(
(settings) => {
this.isDisableCustomScrollbars = !settings.customScrollbars;
}
);
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
if ( this.platform.ANDROID || this.platform.IOS )
/**
* Perfect Scrollbar options
*
* @param value
*/
@Input()
set fusePerfectScrollbarOptions(value)
{
// Merge the options
this._options = _.merge({}, this._options, value);
}
get fusePerfectScrollbarOptions(): any
{
// Return the options
return this._options;
}
/**
* Is enabled
*
* @param {boolean | ""} value
*/
@Input('fusePerfectScrollbar')
set enabled(value: boolean | '')
{
// If nothing is provided with the directive (empty string),
// we will take that as a true
if ( value === '' )
{
this.isMobile = true;
value = true;
}
}
ngAfterViewInit()
{
if ( this.isMobile || this.isDisableCustomScrollbars )
// Return, if both values are the same
if ( this.enabled === value )
{
this.isInitialized = false;
return;
}
// Store the value
this._enabled = value;
// If enabled...
if ( this.enabled )
{
// Init the directive
this._init();
}
else
{
// Otherwise destroy it
this._destroy();
}
}
get enabled(): boolean | ''
{
// Return the enabled status
return this._enabled;
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* After view init
*/
ngAfterViewInit(): void
{
// Check if scrollbars enabled or not from the main config
this._fuseConfigService.config
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(
(settings) => {
this.enabled = settings.customScrollbars;
}
);
// Scroll to the top on every route change
if ( this.fusePerfectScrollbarOptions.updateOnRouteChange )
{
this._router.events
.pipe(
takeUntil(this._unsubscribeAll),
filter(event => event instanceof NavigationEnd)
)
.subscribe(() => {
setTimeout(() => {
this.scrollToTop();
this.update();
}, 0);
});
}
}
/**
* On destroy
*/
ngOnDestroy(): void
{
this._destroy();
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Initialize
*
* @private
*/
_init(): void
{
// Return, if already initialized
if ( this.isInitialized )
{
return;
}
// Check if is mobile
if ( this._platform.ANDROID || this._platform.IOS )
{
this.isMobile = true;
}
// Return if it's mobile
if ( this.isMobile )
{
// Return...
return;
}
// Set as initialized
this.isInitialized = true;
// Initialize the perfect-scrollbar
this.ps = new PerfectScrollbar(this.element.nativeElement, {
wheelPropagation: true
this.ps = new PerfectScrollbar(this.elementRef.nativeElement, {
...this.fusePerfectScrollbarOptions
});
}
ngOnDestroy()
/**
* Destroy
*
* @private
*/
_destroy(): void
{
if ( !this.isInitialized || !this.ps )
{
return;
}
this.onConfigChanged.unsubscribe();
// Destroy the perfect-scrollbar
this.ps.destroy();
// Clean up
this.ps = null;
this.isInitialized = false;
}
/**
* Update scrollbars on window resize
*
* @private
*/
@HostListener('window:resize')
_updateOnResize(): void
{
this._debouncedUpdate();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Document click
*
* @param {Event} event
*/
@HostListener('document:click', ['$event'])
documentClick(event: Event): void
{
@@ -82,7 +257,10 @@ export class FusePerfectScrollbarDirective implements OnInit, AfterViewInit, OnD
this.ps.update();
}
update()
/**
* Update the scrollbar
*/
update(): void
{
if ( !this.isInitialized )
{
@@ -93,62 +271,108 @@ export class FusePerfectScrollbarDirective implements OnInit, AfterViewInit, OnD
this.ps.update();
}
destroy()
/**
* Destroy the scrollbar
*/
destroy(): void
{
this.ngOnDestroy();
}
scrollToX(x: number, speed?: number)
/**
* Scroll to X
*
* @param {number} x
* @param {number} speed
*/
scrollToX(x: number, speed?: number): void
{
this.animateScrolling('scrollLeft', x, speed);
}
scrollToY(y: number, speed?: number)
/**
* Scroll to Y
*
* @param {number} y
* @param {number} speed
*/
scrollToY(y: number, speed?: number): void
{
this.animateScrolling('scrollTop', y, speed);
}
scrollToTop(offset?: number, speed?: number)
/**
* Scroll to top
*
* @param {number} offset
* @param {number} speed
*/
scrollToTop(offset?: number, speed?: number): void
{
this.animateScrolling('scrollTop', (offset || 0), speed);
}
scrollToLeft(offset?: number, speed?: number)
/**
* Scroll to left
*
* @param {number} offset
* @param {number} speed
*/
scrollToLeft(offset?: number, speed?: number): void
{
this.animateScrolling('scrollLeft', (offset || 0), speed);
}
scrollToRight(offset?: number, speed?: number)
/**
* Scroll to right
*
* @param {number} offset
* @param {number} speed
*/
scrollToRight(offset?: number, speed?: number): void
{
const width = this.element.nativeElement.scrollWidth;
const width = this.elementRef.nativeElement.scrollWidth;
this.animateScrolling('scrollLeft', width - (offset || 0), speed);
}
scrollToBottom(offset?: number, speed?: number)
/**
* Scroll to bottom
*
* @param {number} offset
* @param {number} speed
*/
scrollToBottom(offset?: number, speed?: number): void
{
const height = this.element.nativeElement.scrollHeight;
const height = this.elementRef.nativeElement.scrollHeight;
this.animateScrolling('scrollTop', height - (offset || 0), speed);
}
animateScrolling(target: string, value: number, speed?: number)
/**
* Animate scrolling
*
* @param {string} target
* @param {number} value
* @param {number} speed
*/
animateScrolling(target: string, value: number, speed?: number): void
{
if ( !speed )
{
this.element.nativeElement[target] = value;
this.elementRef.nativeElement[target] = value;
// PS has weird event sending order, this is a workaround for that
this.update();
this.update();
}
else if ( value !== this.element.nativeElement[target] )
else if ( value !== this.elementRef.nativeElement[target] )
{
let newValue = 0;
let scrollCount = 0;
let oldTimestamp = performance.now();
let oldValue = this.element.nativeElement[target];
let oldValue = this.elementRef.nativeElement[target];
const cosParameter = (oldValue - value) / 2;
@@ -158,11 +382,11 @@ export class FusePerfectScrollbarDirective implements OnInit, AfterViewInit, OnD
newValue = Math.round(value + cosParameter + cosParameter * Math.cos(scrollCount));
// Only continue animation if scroll position has not changed
if ( this.element.nativeElement[target] === oldValue )
if ( this.elementRef.nativeElement[target] === oldValue )
{
if ( scrollCount >= Math.PI )
{
this.element.nativeElement[target] = value;
this.elementRef.nativeElement[target] = value;
// PS has weird event sending order, this is a workaround for that
this.update();
@@ -171,7 +395,7 @@ export class FusePerfectScrollbarDirective implements OnInit, AfterViewInit, OnD
}
else
{
this.element.nativeElement[target] = oldValue = newValue;
this.elementRef.nativeElement[target] = oldValue = newValue;
oldTimestamp = newTimestamp;

View File

@@ -1,27 +1,8 @@
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { FUSE_CONFIG, FuseConfigService } from '@fuse/services/config.service';
import { FuseCopierService } from '@fuse/services/copier.service';
import { FuseMatchMediaService } from '@fuse/services/match-media.service';
import { FuseMatSidenavHelperService } from '@fuse/directives/fuse-mat-sidenav/fuse-mat-sidenav.service';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
import { FuseSplashScreenService } from '@fuse/services/splash-screen.service';
import { FuseTranslationLoaderService } from '@fuse/services/translation-loader.service';
import { FUSE_CONFIG } from '@fuse/services/config.service';
@NgModule({
entryComponents: [],
providers : [
FuseConfigService,
FuseCopierService,
FuseMatchMediaService,
FuseMatSidenavHelperService,
FuseNavigationService,
FuseSidebarService,
FuseSplashScreenService,
FuseTranslationLoaderService
]
})
@NgModule()
export class FuseModule
{
constructor(@Optional() @SkipSelf() parentModule: FuseModule)

View File

@@ -3,7 +3,14 @@ import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'camelCaseToDash'})
export class CamelCaseToDashPipe implements PipeTransform
{
transform(value: string, args: any[] = [])
/**
* Transform
*
* @param {string} value
* @param {any[]} args
* @returns {string}
*/
transform(value: string, args: any[] = []): string
{
return value ? String(value).replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`) : '';
}

View File

@@ -4,6 +4,14 @@ import { FuseUtils } from '@fuse/utils';
@Pipe({name: 'filter'})
export class FilterPipe implements PipeTransform
{
/**
* Transform
*
* @param {any[]} mainArr
* @param {string} searchText
* @param {string} property
* @returns {any}
*/
transform(mainArr: any[], searchText: string, property: string): any
{
return FuseUtils.filterArrayByString(mainArr, searchText);

View File

@@ -6,6 +6,14 @@ import { Pipe, PipeTransform } from '@angular/core';
})
export class GetByIdPipe implements PipeTransform
{
/**
* Transform
*
* @param {any[]} value
* @param {number} id
* @param {string} property
* @returns {any}
*/
transform(value: any[], id: number, property: string): any
{
const foundItem = value.find(item => {

View File

@@ -3,7 +3,14 @@ import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'htmlToPlaintext'})
export class HtmlToPlaintextPipe implements PipeTransform
{
transform(value: string, args: any[] = [])
/**
* Transform
*
* @param {string} value
* @param {any[]} args
* @returns {string}
*/
transform(value: string, args: any[] = []): string
{
return value ? String(value).replace(/<[^>]+>/gm, '') : '';
}

View File

@@ -3,6 +3,13 @@ import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform
{
/**
* Transform
*
* @param value
* @param {string[]} args
* @returns {any}
*/
transform(value: any, args: string[]): any
{
const keys: any[] = [];

View File

@@ -30,6 +30,7 @@
@import "partials/material";
@import "partials/angular-material-fix";
@import "partials/typography";
@import "partials/docs";
@import "partials/page-layouts";
@import "partials/cards";
@import "partials/navigation";

View File

@@ -84,4 +84,9 @@ mat-chip {
min-width: 0 !important;
min-height: 0 !important;
}
}
// Fix: Mat-card-image requires a bigger width than 100%
.mat-card-image {
max-width: none !important;
}

View File

@@ -14,7 +14,7 @@ i {
}
// Material colors map
$matColorsMap: (
$matPalettes: (
primary: $primary,
accent: $accent,
warn: $warn,
@@ -43,75 +43,91 @@ $matColorsMap: (
);
// Material color hues list
$matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A700;
$matHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A700;
// Text color levels generator mixin
@mixin generateTextColorLevels($baseTextColor) {
@mixin generateTextColorLevels($classes, $contrast) {
// If the base text color is black...
@if (rgba(black, 1) == rgba($baseTextColor, 1)) {
// If the contrast is dark...
@if ($contrast == 'dark') {
i,
.icon {
color: rgba(0, 0, 0, 0.54);
}
// Put down the color classes
#{$classes} {
&.secondary-text,
.secondary-text {
color: rgba(0, 0, 0, 0.54) !important;
}
i,
.icon {
color: rgba(0, 0, 0, 0.54);
}
&.hint-text,
.hint-text,
&.disabled-text,
.disabled-text {
color: rgba(0, 0, 0, 0.38) !important;
}
&.secondary-text,
.secondary-text {
color: rgba(0, 0, 0, 0.54) !important;
}
&.divider,
.divider {
color: rgba(0, 0, 0, 0.12) !important;
}
&.hint-text,
.hint-text,
&.disabled-text,
.disabled-text {
color: rgba(0, 0, 0, 0.38) !important;
}
.mat-ripple-element {
background: rgba(0, 0, 0, 0.1);
&.divider,
.divider {
color: rgba(0, 0, 0, 0.12) !important;
}
.mat-ripple-element {
background: rgba(0, 0, 0, 0.1);
}
.adaptive-border-color {
border-color: rgba(0, 0, 0, 0.12);
}
}
}
// If the base text color is white...
@else {
i,
.icon {
color: rgba(255, 255, 255, 1);
}
// Put down the color classes
#{$classes} {
&.secondary-text,
.secondary-text {
color: rgba(255, 255, 255, 0.70) !important;
}
i,
.icon {
color: rgba(255, 255, 255, 1);
}
&.hint-text,
.hint-text,
&.disabled-text,
.disabled-text {
color: rgba(255, 255, 255, 0.50) !important;
}
&.secondary-text,
.secondary-text {
color: rgba(255, 255, 255, 0.70) !important;
}
&.divider,
.divider {
color: rgba(255, 255, 255, 0.12) !important;
}
&.hint-text,
.hint-text,
&.disabled-text,
.disabled-text {
color: rgba(255, 255, 255, 0.50) !important;
}
.mat-ripple-element {
background: rgba(255, 255, 255, 0.1);
&.divider,
.divider {
color: rgba(255, 255, 255, 0.12) !important;
}
.mat-ripple-element {
background: rgba(255, 255, 255, 0.1);
}
.adaptive-border-color {
border-color: rgba(255, 255, 255, 0.12);
}
}
}
}
@mixin generateMaterialElementColors($contrastColor) {
@mixin generateMaterialElementColors($classes, $contrast) {
// If the contrast color is white...
// If the contrast color is light...
$fuseForeground: (
base: white,
text: white,
@@ -119,8 +135,8 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
divider: rgba(white, 0.12),
);
// If the contrast color is black...
@if (rgba(black, 1) == rgba($contrastColor, 1)) {
// If the contrast color is dark...
@if ($contrast == 'dark') {
$fuseForeground: (
base: black,
@@ -129,43 +145,47 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
);
}
// Native Input
input[type="text"] {
color: map_get($fuseForeground, base);
}
// Put down the color classes
#{$classes} {
// Input
.mat-form-field-label {
color: map_get($fuseForeground, hint-text);
}
// Native Input
input[type="text"] {
color: map_get($fuseForeground, base);
}
.mat-form-field-underline {
background-color: map_get($fuseForeground, divider);
}
// Input
.mat-form-field-label {
color: map_get($fuseForeground, hint-text);
}
// Select
.mat-select-trigger,
.mat-select-arrow {
color: map_get($fuseForeground, hint-text);
}
.mat-form-field-underline {
background-color: map_get($fuseForeground, divider);
}
.mat-select-underline {
background-color: map_get($fuseForeground, divider);
}
// Select
.mat-select-trigger,
.mat-select-arrow {
color: map_get($fuseForeground, hint-text);
}
.mat-select-disabled .mat-select-value,
.mat-select-arrow,
.mat-select-trigger {
color: map_get($fuseForeground, hint-text);
}
.mat-select-underline {
background-color: map_get($fuseForeground, divider);
}
.mat-select-content,
.mat-select-panel-done-animating {
background: map_get($background, card);
}
.mat-select-disabled .mat-select-value,
.mat-select-arrow,
.mat-select-trigger {
color: map_get($fuseForeground, hint-text);
}
.mat-select-value {
color: map_get($fuseForeground, text);
.mat-select-content,
.mat-select-panel-done-animating {
background: map_get($background, card);
}
.mat-select-value {
color: map_get($fuseForeground, text);
}
}
}
@@ -180,14 +200,6 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
background-color: $color !important;
color: $contrastColor !important;
// Generate text color levels
// based on current contrast color
@include generateTextColorLevels($contrastColor);
// Generate material element colors
// based on current contrast color
@include generateMaterialElementColors($contrastColor);
&[disabled] {
background-color: rgba($color, .12) !important;
color: rgba($contrastColor, .26) !important;
@@ -196,14 +208,6 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
.#{$colorName}#{$hue}-fg {
color: $color !important;
// Generate text color levels
// based on current contrast color
@include generateTextColorLevels($color);
// Generate material element colors
// based on current contrast color
@include generateMaterialElementColors($color);
}
.#{$colorName}#{$hue}-border {
@@ -227,22 +231,128 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
}
}
@mixin generateFuseColorClasses($primary, $accent, $warn) {
$palettes: (
primary: $primary,
accent: $accent,
warn: $warn
);
// Define contrast lists
$light-contrasting-classes: ();
$dark-contrasting-classes: ();
// Generate the color classes...
@each $paletteName, $palette in $palettes {
// Get the contrasts map
$contrasts: map-get($palette, 'contrast');
@each $hue in $matHues {
// Get the color and the contrasting color
$color: map-get($palette, $hue);
$contrast: map-get($contrasts, $hue);
@if ($color != null and $contrast != null) {
// Generate color classes
@include generateColorClasses($paletteName, $color, $contrast, '-#{$hue}');
// If the contrast color is dark
@if (rgba(black, 1) == rgba($contrast, 1)) {
$dark-contrasting-classes: append($dark-contrasting-classes, unquote('.mat-#{$paletteName}-#{$hue}-bg'), 'comma');
}
// if the contrast color is light
@else {
$light-contrasting-classes: append($light-contrasting-classes, unquote('.mat-#{$paletteName}-#{$hue}-bg'), 'comma');
}
// Run the generator one more time for default values (500)
@if ($hue == 500) {
// Generate color classes
@include generateColorClasses($paletteName, $color, $contrast, '');
// Add color to the correct list depending on the contrasting color
// If the contrast color is dark
@if (rgba(black, 1) == rgba($contrast, 1)) {
$dark-contrasting-classes: append($dark-contrasting-classes, unquote('.mat-#{$paletteName}-bg'), 'comma');
}
// if the contrast color is light
@else {
$light-contrasting-classes: append($light-contrasting-classes, unquote('.mat-#{$paletteName}-bg'), 'comma');
}
}
}
}
}
// Generate contrasting colors
@include generateTextColorLevels($dark-contrasting-classes, 'dark');
@include generateTextColorLevels($light-contrasting-classes, 'light');
@include generateMaterialElementColors($dark-contrasting-classes, 'dark');
@include generateMaterialElementColors($light-contrasting-classes, 'light');
}
// Generate the color classes...
@each $colorName, $colorMap in $matColorsMap {
@each $hue in $matColorHues {
// Define contrast lists
$light-contrasting-classes: ();
$dark-contrasting-classes: ();
$color: map-get($colorMap, $hue);
$contrastColor: map-get(map-get($colorMap, 'contrast'), $hue);
@each $paletteName, $palette in $matPalettes {
@if ($color != null and $contrastColor != null) {
// Get the contrasts map
$contrasts: map-get($palette, 'contrast');
@include generateColorClasses($colorName, $color, $contrastColor, '-#{$hue}');
@each $hue in $matHues {
// Get the color and the contrasting color
$color: map-get($palette, $hue);
$contrast: map-get($contrasts, $hue);
@if ($color != null and $contrast != null) {
// Generate color classes
@include generateColorClasses($paletteName, $color, $contrast, '-#{$hue}');
// Add color to the correct list depending on the contrasting color
// If the contrast color is dark
@if (rgba(black, 1) == rgba($contrast, 1)) {
$dark-contrasting-classes: append($dark-contrasting-classes, unquote('.mat-#{$paletteName}-#{$hue}-bg'), 'comma');
}
// if the contrast color is light
@else {
$light-contrasting-classes: append($light-contrasting-classes, unquote('.mat-#{$paletteName}-#{$hue}-bg'), 'comma');
}
// Run the generator one more time for default values (500)
@if ($hue == 500) {
@include generateColorClasses($colorName, $color, $contrastColor, '');
// Generate color classes
@include generateColorClasses($paletteName, $color, $contrast, '');
// Add color to the correct list depending on the contrasting color
// If the contrast color is dark
@if (rgba(black, 1) == rgba($contrast, 1)) {
$dark-contrasting-classes: append($dark-contrasting-classes, unquote('.mat-#{$paletteName}-bg'), 'comma');
}
// if the contrast color is light
@else {
$light-contrasting-classes: append($light-contrasting-classes, unquote('.mat-#{$paletteName}-bg'), 'comma');
}
}
}
}
}
// Generate contrasting colors
@include generateTextColorLevels($dark-contrasting-classes, 'dark');
@include generateTextColorLevels($light-contrasting-classes, 'light');
@include generateMaterialElementColors($dark-contrasting-classes, 'dark');
@include generateMaterialElementColors($light-contrasting-classes, 'light');

View File

@@ -0,0 +1,42 @@
.docs {
font-size: 16px;
> .content {
max-width: 980px;
> .main-title {
&:first-child {
margin-top: 0;
}
}
}
.main-title {
display: flex;
margin-top: 72px;
font-size: 24px;
}
.section-title {
display: inline-flex;
font-size: 18px;
margin-top: 24px;
border-bottom: 1px solid #F44336;
color: #F44336;
}
ol,
ul {
padding-left: 24px;
li {
margin-bottom: 12px;
line-height: 1.7;
}
}
p {
line-height: 1.7;
}
}

View File

@@ -1,6 +1,13 @@
html,
body {
> mat-sidenav-container {
height: 100%;
}
}
display: flex;
flex: 1 0 auto;
width: 100%;
height: 100%;
max-height: 100%;
min-height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
background: #F5F5F5;
}

View File

@@ -42,7 +42,7 @@
color: currentColor;
> .nav-link-title {
flex: 1;
flex: 1 1 auto;
white-space: nowrap;
}
@@ -58,7 +58,7 @@
transition: opacity 0.2s ease-in-out 0.1s;
margin-left: 8px;
+ .collapse-arrow {
+ .collapsable-arrow {
margin-left: 8px;
}
}
@@ -72,14 +72,9 @@
}
&.active {
background-color: mat-color($accent);
.mat-ripple-element {
background-color: mat-color($accent, default-contrast, 0.1);
}
&, .nav-link-icon {
color: mat-color($accent, default-contrast);
.nav-link-icon {
opacity: 1;
}
.nav-link-badge {
@@ -90,10 +85,11 @@
.nav-link-icon {
margin-right: 16px;
opacity: 0.7;
}
.nav-link-icon,
.collapse-arrow {
.collapsable-arrow {
font-size: 16px;
width: 16px;
height: 16px;
@@ -103,7 +99,7 @@
}
}
&.nav-collapse {
&.nav-collapsable {
display: block;
> .children {
@@ -135,7 +131,7 @@
> .nav-item {
&.nav-collapse {
&.nav-collapsable {
background: transparent;
transition: background 200ms ease-in-out;
@@ -149,7 +145,7 @@
> .group-items {
> .nav-collapse {
> .nav-collapsable {
background: transparent;
transition: background 200ms ease-in-out;
@@ -176,7 +172,7 @@
.nav-item {
&.nav-collapse {
&.nav-collapsable {
position: relative;
.children {
@@ -205,13 +201,13 @@
height: 56px;
}
&.nav-collapse {
&.nav-collapsable {
position: relative;
> .nav-link {
height: 56px;
.collapse-arrow {
.collapsable-arrow {
display: none;
}
}
@@ -223,4 +219,26 @@
}
}
}
// Material style
&.material {
.nav-subheader {
border-top: 1px solid rgba(0, 0, 0, 0.12);
&:first-child {
border-top: none;
}
}
.nav-item {
.nav-link {
height: 40px;
padding: 0 16px;
margin: 4px 8px;
border-radius: 4px;
}
}
}
}

View File

@@ -10,13 +10,9 @@ $header-height-sm: 100px !default;
$carded-header-height-without-toolbar: $carded-header-height - $carded-toolbar-height;
$carded-header-height-without-toolbar-sm: $carded-header-height-sm - $carded-toolbar-height;
// Top bg image
$top-bg-image: url('assets/images/backgrounds/header-bg.png');
.page-layout {
position: relative;
overflow-x: hidden;
overflow-y: auto;
overflow: hidden;
// Carded layout
&.carded {
@@ -27,15 +23,13 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
min-width: 100%;
// Top bg
.top-bg {
> .top-bg {
position: absolute;
z-index: 1;
top: 0;
right: 0;
left: 0;
height: $carded-header-height;
background-image: $top-bg-image;
background-size: cover;
@include media-breakpoint-down('sm') {
height: $carded-header-height-sm;
@@ -45,48 +39,39 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
// Fullwidth
&.fullwidth {
// Single scroll
&.single-scroll {
> .center {
flex: 1 0 auto;
max-height: none;
}
}
// Center
> .center {
display: flex;
flex-direction: column;
flex: 1;
flex: 1 0 auto;
position: relative;
z-index: 2;
padding: 0 32px;
width: 100%;
min-width: 100%;
min-width: 0;
max-width: 100%;
max-height: 100%;
.header {
height: $carded-header-height-without-toolbar;
min-height: $carded-header-height-without-toolbar;
max-height: $carded-header-height-without-toolbar;
> .header {
height: $carded-header-height-without-toolbar !important;
min-height: $carded-header-height-without-toolbar !important;
max-height: $carded-header-height-without-toolbar !important;
@include media-breakpoint-down('sm') {
height: $carded-header-height-without-toolbar-sm;
min-height: $carded-header-height-without-toolbar-sm;
max-height: $carded-header-height-without-toolbar-sm;
height: $carded-header-height-without-toolbar-sm !important;
min-height: $carded-header-height-without-toolbar-sm !important;
max-height: $carded-header-height-without-toolbar-sm !important;
}
}
.content-card {
> .content-card {
display: flex;
flex-direction: column;
flex: 1;
flex: 1 0 auto;
overflow: hidden;
@include mat-elevation(7);
.toolbar {
> .toolbar {
display: flex;
justify-content: flex-start;
align-items: center;
@@ -97,128 +82,7 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
}
> .content {
display: flex;
flex: 1;
overflow: auto;
}
}
}
}
// Left sidenav - Right sidenav
&.left-sidenav,
&.right-sidenav {
// Single scroll
&.single-scroll {
> mat-sidenav-container {
flex: 1 0 auto;
}
}
> mat-sidenav-container {
display: flex;
flex: 1;
background: none;
z-index: 2;
width: 100%;
.sidenav {
display: flex;
flex-direction: column;
flex: 1;
width: 240px;
min-width: 240px;
max-width: 240px;
height: auto;
z-index: 4;
overflow-y: hidden;
@include mat-elevation(7);
&.mat-is-locked-open {
background: none;
box-shadow: none;
}
.header {
height: $carded-header-height;
min-height: $carded-header-height;
max-height: $carded-header-height;
@include media-breakpoint-down('sm') {
height: $carded-header-height-sm;
min-height: $carded-header-height-sm;
max-height: $carded-header-height-sm;
}
}
.content {
background: transparent;
overflow: auto;
}
}
> .mat-sidenav-content,
> .mat-drawer-content {
display: flex;
flex: 1;
height: auto;
overflow: visible;
// Center
.center {
display: flex;
flex-direction: column;
flex: 1;
position: relative;
z-index: 3;
margin-left: 32px;
margin-right: 32px;
.header {
display: flex;
height: $carded-header-height-without-toolbar;
min-height: $carded-header-height-without-toolbar;
max-height: $carded-header-height-without-toolbar;
@include media-breakpoint-down('sm') {
height: $carded-header-height-without-toolbar-sm;
min-height: $carded-header-height-without-toolbar-sm;
max-height: $carded-header-height-without-toolbar-sm;
}
}
.content-card {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
@include mat-elevation(7);
.toolbar {
display: flex;
justify-content: flex-start;
align-items: center;
flex: 1;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
height: $carded-toolbar-height;
min-height: $carded-toolbar-height;
max-height: $carded-toolbar-height;
.sidenav-toggle {
margin: 0 8px 0 0 !important;
padding: 0 !important;
border-radius: 0;
}
}
.content {
display: flex;
flex: 1;
overflow: auto;
}
}
flex: 1 0 auto;
}
}
}
@@ -226,51 +90,82 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
// Tabbed
&.tabbed {
> mat-sidenav-container {
> .center {
width: 100%;
min-width: 0;
> .mat-sidenav-content,
> .mat-drawer-content {
width: calc(100% - 240px);
min-width: 0;
> .header {
flex: 1 1 auto;
}
.center {
width: calc(100% - 32px);
min-width: 0;
> .content-card {
@include media-breakpoint-down('md') {
width: calc(100% - 64px);
}
> .content {
display: flex;
.header {
flex: 1;
}
> .mat-tab-group {
overflow: hidden;
.content-card {
.mat-tab-header {
.content {
.mat-tab-label {
height: 64px;
}
}
.mat-tab-group {
.mat-tab-body {
overflow: hidden;
.mat-tab-body-content {
overflow: hidden;
.mat-tab-header {
.mat-tab-label {
height: 64px;
}
.tab-content {
position: relative;
width: 100%;
height: 100%;
}
}
}
}
}
}
}
}
.mat-tab-body {
overflow: hidden;
// Inner scroll
&.inner-scroll {
flex: 1 1 auto;
.mat-tab-body-content {
overflow: hidden;
> .center {
flex: 1 1 auto;
.tab-content {
position: relative;
width: 100%;
height: 100%;
overflow: auto;
}
> .content-card {
flex: 1 1 auto;
> .content {
overflow: auto;
flex: 1 1 auto;
}
}
}
// Tabbed
&.tabbed {
> .center {
> .content-card {
> .content {
> .mat-tab-group {
.mat-tab-body {
.mat-tab-body-content {
.tab-content {
overflow: auto;
}
}
}
@@ -282,21 +177,212 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
}
}
// Left sidenav
&.left-sidenav {
// Left / Right sidebar
&.left-sidebar,
&.right-sidebar {
flex-direction: row;
// Sidenav
> mat-sidenav-container {
// Sidebar
> .sidebar {
display: flex;
flex-direction: column;
flex: 1 1 auto;
width: 240px;
min-width: 240px;
max-width: 240px;
height: auto;
overflow: hidden;
@include mat-elevation(7);
.sidenav {
&.locked-open {
background: none;
box-shadow: none;
&.mat-is-locked-open {
+ .center {
z-index: 1001;
}
~ .mat-sidenav-content,
~ .mat-drawer-content {
&.left-positioned {
.center {
margin-left: 0;
+ .center {
margin-left: 0;
}
}
&.right-positioned {
+ .center {
margin-right: 0;
}
}
}
.header {
height: $carded-header-height;
min-height: $carded-header-height;
max-height: $carded-header-height;
@include media-breakpoint-down('sm') {
height: $carded-header-height-sm;
min-height: $carded-header-height-sm;
max-height: $carded-header-height-sm;
}
}
.content {
background: transparent;
flex: 1 1 auto;
}
}
// Center
> .center {
display: flex;
flex-direction: column;
flex: 1 1 auto;
position: relative;
z-index: 3;
margin-left: 32px;
margin-right: 32px;
min-width: 0;
> .header {
display: flex;
height: $carded-header-height-without-toolbar;
min-height: $carded-header-height-without-toolbar;
max-height: $carded-header-height-without-toolbar;
@include media-breakpoint-down('sm') {
height: $carded-header-height-without-toolbar-sm;
min-height: $carded-header-height-without-toolbar-sm;
max-height: $carded-header-height-without-toolbar-sm;
}
}
> .content-card {
display: flex;
flex-direction: column;
flex: 1 1 auto;
overflow: hidden;
@include mat-elevation(7);
> .toolbar {
display: flex;
justify-content: flex-start;
align-items: center;
flex: 1 1 auto;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
height: $carded-toolbar-height;
min-height: $carded-toolbar-height;
max-height: $carded-toolbar-height;
.sidebar-toggle {
margin: 0 8px 0 0 !important;
padding: 0 !important;
border-radius: 0;
}
}
> .content {
flex: 1 1 auto;
}
}
}
// Tabbed
&.tabbed {
> .center {
width: calc(100% - 32px);
min-width: 0;
@include media-breakpoint-down('md') {
width: calc(100% - 64px);
}
> .header {
flex: 1 1 auto;
}
> .content-card {
> .content {
display: flex;
> .mat-tab-group {
overflow: hidden;
.mat-tab-header {
.mat-tab-label {
height: 64px;
}
}
.mat-tab-body {
overflow: hidden;
.mat-tab-body-content {
overflow: hidden;
.tab-content {
position: relative;
width: 100%;
height: 100%;
}
}
}
}
}
}
}
}
// Inner scroll
&.inner-scroll {
flex: 1 1 auto;
> .sidebar {
.content {
overflow: auto;
}
}
> .center {
flex: 1 1 auto;
> .content-card {
flex: 1 1 auto;
> .content {
flex: 1 1 auto;
overflow: auto;
}
}
}
// Tabbed
&.tabbed {
> .center {
> .content-card {
> .content {
> .mat-tab-group {
.mat-tab-body {
.mat-tab-body-content {
.tab-content {
overflow: auto;
}
}
}
}
}
}
}
@@ -304,26 +390,15 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
}
}
// Right sidenav
&.right-sidenav {
// Right sidebar specific
&.right-sidebar {
// Sidenav
> mat-sidenav-container {
> .sidebar {
order: 2;
}
.sidenav {
order: 999;
&.mat-is-locked-open {
~ .mat-sidenav-content,
~ .mat-drawer-content {
.center {
margin-right: 0;
}
}
}
}
> .center {
order: 1;
}
}
}
@@ -336,20 +411,17 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
width: 100%;
min-width: 100%;
// Top bg
> .header {
background-image: $top-bg-image;
background-size: cover;
}
// Fullwidth
&.fullwidth {
overflow: auto;
> .content {
flex: 1 1 auto;
min-width: 0;
}
}
&.fullwidth,
&.inner-sidenav {
min-height: 100%;
&.inner-sidebar {
> .header {
height: $header-height;
@@ -358,109 +430,138 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
}
}
// Left sidenav - Right sidenav
&.left-sidenav,
&.right-sidenav {
// Left / Right sidebar
&.left-sidebar,
&.right-sidebar {
flex-direction: row;
// Single scroll
&.single-scroll {
// Sidebar
> .sidebar {
width: 240px;
min-width: 240px;
max-width: 240px;
overflow: hidden;
@include mat-elevation(7);
> mat-sidenav-container {
flex: 1 0 auto;
&.locked-open {
background: none;
box-shadow: none;
> .mat-sidenav-content,
> .mat-drawer-content {
flex: 1 0 auto;
max-height: none;
+ .center {
z-index: 1001;
}
}
}
// Inner Sidenav
&.inner-sidenav {
&.left-positioned {
> mat-sidenav-container {
flex: 1;
.sidenav {
.sidenav-content {
height: 100%;
+ .center {
margin-left: 0;
}
}
> .mat-sidenav-content,
> .mat-drawer-content {
display: flex;
height: auto;
&.right-positioned {
.center {
flex: 1;
min-height: 100%;
@include mat-elevation(0);
.content {
display: flex;
flex: 1 0 auto;
}
+ .center {
margin-right: 0;
}
}
}
.content {
flex: 1 1 auto;
}
}
> mat-sidenav-container {
// Center
> .center {
position: relative;
display: flex;
flex-direction: column;
flex: 1;
background: none;
z-index: 2;
width: 100%;
flex: 1 1 auto;
z-index: 3;
min-width: 0;
@include mat-elevation(7);
.sidenav {
width: 240px;
min-width: 240px;
max-width: 240px;
z-index: 51;
@include mat-elevation(7);
> .header {
height: $header-height;
min-height: $header-height;
max-height: $header-height;
}
&.mat-is-locked-open {
width: 220px;
min-width: 220px;
max-width: 220px;
box-shadow: none;
background: transparent;
}
> .content {
flex: 1 0 auto;
}
}
.sidenav-content {
height: 100%;
// Inner scroll
&.inner-scroll {
flex: 1 1 auto;
> .sidebar {
.content {
overflow: auto;
}
}
> .mat-sidenav-content,
> .mat-drawer-content {
> .center {
flex: 1 1 auto;
overflow: auto;
}
}
// Inner Sidebar
&.inner-sidebar {
flex-direction: column;
overflow: hidden;
height: 100%;
> .content {
display: flex;
flex: 1;
height: auto;
overflow: visible;
max-height: 100%;
min-height: 0;
.header {
height: $header-height;
min-height: $header-height;
max-height: $header-height;
background-image: $top-bg-image;
}
> .sidebar {
.center {
display: flex;
flex-direction: column;
flex: 1;
overflow: auto;
@include mat-elevation(7);
&.locked-open {
background: none;
box-shadow: none;
}
.content {
overflow: auto;
}
}
> .center {
flex: 1 1 auto;
overflow: auto;
}
}
}
}
// Right sidebar specific
&.right-sidebar {
> .sidebar {
order: 2;
}
> .center {
order: 1;
}
// Inner sidebar
&.inner-sidebar {
> .content {
> .sidebar {
order: 2;
}
> .center {
order: 1;
}
}
}
}
@@ -469,7 +570,7 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
&.tabbed {
min-height: 100%;
.header {
> .header {
height: $header-height;
min-height: $header-height;
max-height: $header-height;
@@ -477,7 +578,7 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
> .content {
.mat-tab-group {
> .mat-tab-group {
.mat-tab-labels {
padding: 0 24px;
@@ -495,50 +596,6 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
@include media-breakpoint-down('xs') {
// Activate single-scroll
&.carded {
&.fullwidth {
> .center {
flex: 1 0 auto;
max-height: none;
}
}
&.left-sidenav,
&.right-sidenav {
> mat-sidenav-container {
flex: 1 0 auto;
}
}
}
&.simple {
&.fullwidth {
> .content {
flex: 1 0 auto;
}
}
&.left-sidenav,
&.right-sidenav {
> mat-sidenav-container {
flex: 1 0 auto !important;
> .mat-sidenav-content,
> .mat-drawer-content {
flex: 1 0 auto;
}
}
}
}
// End - Activate single-scroll
// Smaller margins
&.carded {
@@ -549,18 +606,11 @@ $top-bg-image: url('assets/images/backgrounds/header-bg.png');
}
}
&.left-sidenav,
&.right-sidenav {
&.left-sidebar,
&.right-sidebar {
> mat-sidenav-container {
> .mat-sidenav-content,
> .mat-drawer-content {
.center {
margin: 0 16px;
}
}
> .center {
margin: 0 16px;
}
}
}

View File

@@ -34,13 +34,13 @@
}
/* General styles */
fuse-root {
app {
fuse-navbar-vertical,
fuse-navbar-horizontal,
fuse-toolbar,
fuse-footer,
fuse-quick-panel,
fuse-sidebar,
navbar,
toolbar,
footer,
.theme-options-button,
fuse-theme-options,
.ps > .ps__rail-x,
.ps > .ps__rail-y {

View File

@@ -274,4 +274,119 @@ strong {
// Nowrap
.text-nowrap {
white-space: nowrap;
}
// Changelog
.changelog {
.entry {
background: white;
margin-bottom: 24px;
padding: 24px 32px;
@include mat-elevation(2);
> .title {
display: flex;
align-items: center;
margin-bottom: 24px;
.version {
font-size: 24px;
}
.date {
margin-left: 8px;
font-size: 17px;
opacity: 0.54;
}
}
.groups {
div {
margin-bottom: 32px;
&:last-child {
margin-bottom: 0;
}
}
.title {
display: inline-flex;
font-size: 13px;
color: white;
letter-spacing: 0.015em;
line-height: 1;
padding: 5px 8px;
border-radius: 2px;
}
.breaking-changes {
.title {
background: #F44336;
}
}
.new {
.title {
background: #43A047;
}
}
.improved {
.title {
background: #673AB7;
}
}
.fixed {
.title {
background: #2196F3;
}
}
ul {
padding-left: 24px;
li {
margin-bottom: 6px;
letter-spacing: 0.015em;
}
}
}
}
}
// Message boxes
.message-box {
padding: 16px;
background: #607D8B;
border-left: 6px solid #37474F;
color: rgba(255, 255, 255, 1);
&.error {
background: #EF5350;
border-left-color: #B71C1C;
}
&.warning {
background: #FFECB3;
border-left-color: #FFC107;
color: rgba(0, 0, 0, 0.87);
}
&.success {
background: #4CAF50;
border-left-color: #2E7D32;
}
&.info {
background: #B3E5FC;
border-left-color: #03A9F4;
color: rgba(0, 0, 0, 0.87);
}
}

View File

@@ -104,9 +104,9 @@ $code-color-attr-name: #B65611 !default;
// whitespace management
white-space: pre; // fallback
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
//white-space: pre-wrap;
//word-break: break-all;
//word-wrap: break-word;
font-family: $code-font-family;
font-size: $code-font-size;

View File

@@ -1,93 +1,157 @@
import { Inject, Injectable, InjectionToken } from '@angular/core';
import { NavigationEnd, NavigationStart, Router } from '@angular/router';
import { NavigationStart, Router } from '@angular/router';
import { Platform } from '@angular/cdk/platform';
import { BehaviorSubject } from 'rxjs';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import * as _ from 'lodash';
// Create the injection token for the custom config
// Create the injection token for the custom settings
export const FUSE_CONFIG = new InjectionToken('fuseCustomConfig');
@Injectable()
@Injectable({
providedIn: 'root'
})
export class FuseConfigService
{
config: any;
defaultConfig: any;
isSetConfigRan = false;
onConfigChanged: BehaviorSubject<any>;
// Private
private _configSubject: BehaviorSubject<any>;
private readonly _defaultConfig: any;
/**
* Constructor
*
* @param router
* @param platform
* @param config
* @param {Platform} _platform
* @param {Router} _router
* @param _config
*/
constructor(
private router: Router,
public platform: Platform,
@Inject(FUSE_CONFIG) config
private _platform: Platform,
private _router: Router,
@Inject(FUSE_CONFIG) private _config
)
{
// Set the default config from the user provided one (forRoot)
this.defaultConfig = config;
// Set the default config from the user provided config (from forRoot)
this._defaultConfig = _config;
/**
* Disable Custom Scrollbars if Browser is Mobile
*/
if ( this.platform.ANDROID || this.platform.IOS )
{
this.defaultConfig.customScrollbars = false;
}
// Initialize the service
this._init();
}
// Set the config from the default config
this.config = _.cloneDeep(this.defaultConfig);
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
// Reload the default settings for the
// layout on every navigation start
router.events.subscribe(
(event) => {
/**
* Set and get the config
*/
set config(value)
{
// Get the value from the behavior subject
let config = this._configSubject.getValue();
if ( event instanceof NavigationStart )
{
this.isSetConfigRan = false;
}
// Merge the new config
config = _.merge({}, config, value);
if ( event instanceof NavigationEnd )
{
if ( this.isSetConfigRan )
{
return;
}
// Notify the observers
this._configSubject.next(config);
}
this.setConfig({
layout: this.defaultConfig.layout
}
);
}
}
);
// Create the behavior subject
this.onConfigChanged = new BehaviorSubject(this.config);
get config(): any | Observable<any>
{
return this._configSubject.asObservable();
}
/**
* Set the new config from given object
* Get default config
*
* @param config
* @returns {any}
*/
setConfig(config): void
get defaultConfig(): any
{
// Set the SetConfigRan true
this.isSetConfigRan = true;
return this._defaultConfig;
}
// Merge the config
this.config = _.merge({}, this.config, config);
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
// Trigger the event
this.onConfigChanged.next(this.config);
/**
* Initialize
*
* @private
*/
private _init(): void
{
/**
* Disable custom scrollbars if browser is mobile
*/
if ( this._platform.ANDROID || this._platform.IOS )
{
this._defaultConfig.customScrollbars = false;
}
// Set the config from the default config
this._configSubject = new BehaviorSubject(_.cloneDeep(this._defaultConfig));
// Reload the default config on every navigation start if
// the current config is different from the default one
this._router.events
.pipe(filter(event => event instanceof NavigationStart))
.subscribe(() => {
if ( !_.isEqual(this._configSubject.getValue(), this._defaultConfig) )
{
// Clone the default config
const config = _.cloneDeep(this._defaultConfig);
// Set the config
this._configSubject.next(config);
}
});
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Set config
*
* @param value
* @param {{emitEvent: boolean}} opts
*/
setConfig(value, opts = {emitEvent: true}): void
{
// Get the value from the behavior subject
let config = this._configSubject.getValue();
// Merge the new config
config = _.merge({}, config, value);
// If emitEvent option is true...
if ( opts.emitEvent === true )
{
// Notify the observers
this._configSubject.next(config);
}
}
/**
* Get config
*
* @returns {Observable<any>}
*/
getConfig(): Observable<any>
{
return this._configSubject.asObservable();
}
/**
* Reset to the default config
*/
resetToDefaults(): void
{
// Set the config from the default config
this._configSubject.next(_.cloneDeep(this._defaultConfig));
}
}

View File

@@ -1,26 +1,31 @@
/**
* This class is based on the code in the following projects:
*
* - https://github.com/zenorocha/select
* - https://github.com/zenorocha/clipboard.js/
* https://github.com/zenorocha/select
* https://github.com/zenorocha/clipboard.js/
*
* Both released under MIT license - © Zeno Rocha
*/
import { Injectable } from '@angular/core';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class FuseCopierService
{
private textarea: HTMLTextAreaElement;
/** Copy the text value to the clipboard. */
/**
* Copy the text value to the clipboard
*
* @param {string} text
* @returns {boolean}
*/
copyText(text: string): boolean
{
this.createTextareaAndSelect(text);
const copySuccessful = document.execCommand('copy');
this.removeFake();
this._removeFake();
return copySuccessful;
}
@@ -28,8 +33,10 @@ export class FuseCopierService
/**
* Creates a hidden textarea element, sets its value from `text` property,
* and makes a selection on it.
*
* @param {string} text
*/
private createTextareaAndSelect(text: string)
private createTextareaAndSelect(text: string): void
{
// Create a fake element to hold the contents to copy
this.textarea = document.createElement('textarea');
@@ -53,8 +60,12 @@ export class FuseCopierService
this.textarea.setSelectionRange(0, this.textarea.value.length);
}
/** Remove the text area from the DOM. */
private removeFake()
/**
* Remove the text area from the DOM
*
* @private
*/
private _removeFake(): void
{
if ( this.textarea )
{

View File

@@ -0,0 +1,91 @@
import { Injectable } from '@angular/core';
import { NavigationEnd, NavigationStart, Router } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class FuseLoadingBarService
{
// Private
private _visible: BehaviorSubject<boolean>;
/**
* Constructor
*
* @param {Router} _router
*/
constructor(
private _router: Router
)
{
// Initialize the service
this._init();
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
get visible(): Observable<any>
{
// Return the _visible as observable
return this._visible.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Initialize
*
* @private
*/
private _init(): void
{
// Initialize the behavior subject
this._visible = new BehaviorSubject(false);
// Subscribe to the router events to show/hide the loading bar
this._router.events
.pipe(
filter((event) => event instanceof NavigationStart)
)
.subscribe(() => {
this.showLoadingBar();
});
this._router.events
.pipe(
filter((event) => event instanceof NavigationEnd)
)
.subscribe(() => {
this.hideLoadingBar();
});
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Show the loading bar
*/
showLoadingBar(): void
{
// Show
this._visible.next(true);
}
/**
* Hide the loading bar
*/
hideLoadingBar(): void
{
// Hide
this._visible.next(false);
}
}

View File

@@ -2,22 +2,50 @@ import { MediaChange, ObservableMedia } from '@angular/flex-layout';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class FuseMatchMediaService
{
activeMediaQuery: string;
onMediaChange: BehaviorSubject<string> = new BehaviorSubject<string>('');
constructor(private observableMedia: ObservableMedia)
/**
* Constructor
*
* @param {ObservableMedia} _observableMedia
*/
constructor(
private _observableMedia: ObservableMedia
)
{
// Set the defaults
this.activeMediaQuery = '';
this.observableMedia.subscribe((change: MediaChange) => {
if ( this.activeMediaQuery !== change.mqAlias )
{
this.activeMediaQuery = change.mqAlias;
this.onMediaChange.next(change.mqAlias);
}
});
// Initialize
this._init();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Initialize
*
* @private
*/
private _init(): void
{
this._observableMedia
.subscribe((change: MediaChange) => {
if ( this.activeMediaQuery !== change.mqAlias )
{
this.activeMediaQuery = change.mqAlias;
this.onMediaChange.next(change.mqAlias);
}
});
}
}

View File

@@ -3,26 +3,50 @@ import { DOCUMENT } from '@angular/common';
import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
import { NavigationEnd, Router } from '@angular/router';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class FuseSplashScreenService
{
splashScreenEl;
public player: AnimationPlayer;
splashScreenEl: any;
player: AnimationPlayer;
/**
* Constructor
*
* @param {AnimationBuilder} _animationBuilder
* @param _document
* @param {Router} _router
*/
constructor(
private animationBuilder: AnimationBuilder,
@Inject(DOCUMENT) private document: any,
private router: Router
private _animationBuilder: AnimationBuilder,
@Inject(DOCUMENT) private _document: any,
private _router: Router
)
{
// Initialize
this._init();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Initialize
*
* @private
*/
private _init(): void
{
// Get the splash screen element
this.splashScreenEl = this.document.body.querySelector('#fuse-splash-screen');
this.splashScreenEl = this._document.body.querySelector('#fuse-splash-screen');
// If the splash screen element exists...
if ( this.splashScreenEl )
{
// Hide it on the first NavigationEnd event
const hideOnLoad = this.router.events.subscribe((event) => {
const hideOnLoad = this._router.events.subscribe((event) => {
if ( event instanceof NavigationEnd )
{
setTimeout(() => {
@@ -38,10 +62,17 @@ export class FuseSplashScreenService
}
}
show()
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Show the splash screen
*/
show(): void
{
this.player =
this.animationBuilder
this._animationBuilder
.build([
style({
opacity: '0',
@@ -55,10 +86,13 @@ export class FuseSplashScreenService
}, 0);
}
hide()
/**
* Hide the splash screen
*/
hide(): void
{
this.player =
this.animationBuilder
this._animationBuilder
.build([
style({opacity: '1'}),
animate('400ms ease', style({

View File

@@ -7,21 +7,39 @@ export interface Locale
data: Object;
}
@Injectable()
@Injectable({
providedIn: 'root'
})
export class FuseTranslationLoaderService
{
constructor(private translate: TranslateService)
/**
* Constructor
*
* @param {TranslateService} _translateService
*/
constructor(
private _translateService: TranslateService
)
{
}
public loadTranslations(...args: Locale[]): void
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Load translations
*
* @param {Locale} args
*/
loadTranslations(...args: Locale[]): void
{
const locales = [...args];
locales.forEach((locale) => {
// use setTranslation() with the third argument set to true
// to append translations instead of replacing them
this.translate.setTranslation(locale.lang, locale.data, true);
this._translateService.setTranslation(locale.lang, locale.data, true);
});
}
}

View File

@@ -0,0 +1,29 @@
export interface FuseConfig
{
layout: {
style: string,
width: 'fullwidth' | 'boxed',
navbar: {
background: string,
hidden: boolean,
folded: boolean,
position: 'left' | 'right' | 'top',
variant: string
},
toolbar: {
background: string,
hidden: boolean,
position: 'above' | 'above-static' | 'above-fixed' | 'below' | 'below-static' | 'below-fixed'
}
footer: {
background: string,
hidden: boolean,
position: 'above' | 'above-static' | 'above-fixed' | 'below' | 'below-static' | 'below-fixed'
},
sidepanel: {
hidden: boolean,
position: 'left' | 'right'
}
};
customScrollbars: boolean;
}

View File

@@ -0,0 +1,27 @@
export interface FuseNavigationItem
{
id: string;
title: string;
type: 'item' | 'group' | 'collapsable';
translate?: string;
icon?: string;
hidden?: boolean;
url?: string;
classes?: string;
exactMatch?: boolean;
externalUrl?: boolean;
openInNewTab?: boolean;
function?: any;
badge?: {
title?: string;
translate?: string;
bg?: string;
fg?: string;
};
children?: FuseNavigationItem[];
}
export interface FuseNavigation extends FuseNavigationItem
{
children?: FuseNavigationItem[];
}

2
src/@fuse/types/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './fuse-config';
export * from './fuse-navigation';

View File

@@ -1,6 +1,13 @@
export class FuseUtils
{
public static filterArrayByString(mainArr, searchText)
/**
* Filter array by string
*
* @param mainArr
* @param searchText
* @returns {any}
*/
public static filterArrayByString(mainArr, searchText): any
{
if ( searchText === '' )
{
@@ -14,7 +21,14 @@ export class FuseUtils
});
}
public static searchInObj(itemObj, searchText)
/**
* Search in object
*
* @param itemObj
* @param searchText
* @returns {boolean}
*/
public static searchInObj(itemObj, searchText): boolean
{
for ( const prop in itemObj )
{
@@ -51,7 +65,14 @@ export class FuseUtils
}
}
public static searchInArray(arr, searchText)
/**
* Search in array
*
* @param arr
* @param searchText
* @returns {boolean}
*/
public static searchInArray(arr, searchText): boolean
{
for ( const value of arr )
{
@@ -73,14 +94,26 @@ export class FuseUtils
}
}
public static searchInString(value, searchText)
/**
* Search in string
*
* @param value
* @param searchText
* @returns {any}
*/
public static searchInString(value, searchText): any
{
return value.toLowerCase().includes(searchText);
}
public static generateGUID()
/**
* Generate a unique GUID
*
* @returns {string}
*/
public static generateGUID(): string
{
function S4()
function S4(): string
{
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
@@ -90,7 +123,13 @@ export class FuseUtils
return S4() + S4();
}
public static toggleInArray(item, array)
/**
* Toggle in array
*
* @param item
* @param array
*/
public static toggleInArray(item, array): void
{
if ( array.indexOf(item) === -1 )
{
@@ -102,7 +141,13 @@ export class FuseUtils
}
}
public static handleize(text)
/**
* Handleize
*
* @param text
* @returns {string}
*/
public static handleize(text): string
{
return text.toString().toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -

View File

@@ -1 +1,29 @@
<fuse-main></fuse-main>
<!-- VERTICAL LAYOUT 1 -->
<ng-container *ngIf="fuseConfig.layout.style === 'vertical-layout-1'">
<vertical-layout-1></vertical-layout-1>
</ng-container>
<!-- VERTICAL LAYOUT 2 -->
<ng-container *ngIf="fuseConfig.layout.style === 'vertical-layout-2'">
<vertical-layout-2></vertical-layout-2>
</ng-container>
<!-- VERTICAL LAYOUT 3 -->
<ng-container *ngIf="fuseConfig.layout.style === 'vertical-layout-3'">
<vertical-layout-3></vertical-layout-3>
</ng-container>
<!-- HORIZONTAL LAYOUT 1 -->
<ng-container *ngIf="fuseConfig.layout.style === 'horizontal-layout-1'">
<horizontal-layout-1></horizontal-layout-1>
</ng-container>
<!-- THEME OPTIONS PANEL -->
<button mat-icon-button class="mat-primary-bg mat-elevation-z2 theme-options-button"
(click)="toggleSidebarOpen('themeOptionsPanel')">
<mat-icon>settings</mat-icon>
</button>
<fuse-sidebar name="themeOptionsPanel" class="theme-options-sidebar" position="right" [invisibleOverlay]="true">
<fuse-theme-options></fuse-theme-options>
</fuse-sidebar>

View File

@@ -0,0 +1,40 @@
@import "src/@fuse/scss/fuse";
:host {
position: relative;
display: flex;
flex: 1 1 auto;
width: 100%;
height: 100%;
min-width: 0;
.theme-options-button {
position: absolute;
top: 160px;
right: 0;
width: 48px;
height: 48px;
line-height: 48px;
text-align: center;
cursor: pointer;
border-radius: 0;
margin: 0;
pointer-events: auto;
opacity: .90;
z-index: 998;
mat-icon {
animation: rotating 3s linear infinite;
}
&:hover {
opacity: 1;
}
}
.theme-options-sidebar {
width: 360px;
min-width: 360px;
max-width: 360px;
}
}

View File

@@ -1,37 +1,125 @@
import { Component } from '@angular/core';
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Platform } from '@angular/cdk/platform';
import { TranslateService } from '@ngx-translate/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseConfigService } from '@fuse/services/config.service';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
import { FuseSplashScreenService } from '@fuse/services/splash-screen.service';
import { FuseTranslationLoaderService } from '@fuse/services/translation-loader.service';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { locale as navigationEnglish } from './navigation/i18n/en';
import { locale as navigationTurkish } from './navigation/i18n/tr';
import { navigation } from 'app/navigation/navigation';
import { locale as navigationEnglish } from 'app/navigation/i18n/en';
import { locale as navigationTurkish } from 'app/navigation/i18n/tr';
@Component({
selector : 'fuse-root',
selector : 'app',
templateUrl: './app.component.html',
styleUrls : ['./app.component.scss']
})
export class AppComponent
export class AppComponent implements OnInit, OnDestroy
{
navigation: any;
fuseConfig: any;
// Private
private _unsubscribeAll: Subject<any>;
/**
* Constructor
*
* @param {DOCUMENT} document
* @param {FuseConfigService} _fuseConfigService
* @param {FuseNavigationService} _fuseNavigationService
* @param {FuseSidebarService} _fuseSidebarService
* @param {FuseSplashScreenService} _fuseSplashScreenService
* @param {FuseTranslationLoaderService} _fuseTranslationLoaderService
* @param {Platform} _platform
* @param {TranslateService} _translateService
*/
constructor(
private translate: TranslateService,
private fuseNavigationService: FuseNavigationService,
private fuseSplashScreen: FuseSplashScreenService,
private fuseTranslationLoader: FuseTranslationLoaderService
@Inject(DOCUMENT) private document: any,
private _fuseConfigService: FuseConfigService,
private _fuseNavigationService: FuseNavigationService,
private _fuseSidebarService: FuseSidebarService,
private _fuseSplashScreenService: FuseSplashScreenService,
private _fuseTranslationLoaderService: FuseTranslationLoaderService,
private _translateService: TranslateService,
private _platform: Platform
)
{
// Get default navigation
this.navigation = navigation;
// Register the navigation to the service
this._fuseNavigationService.register('main', this.navigation);
// Set the main navigation as our current navigation
this._fuseNavigationService.setCurrentNavigation('main');
// Add languages
this.translate.addLangs(['en', 'tr']);
this._translateService.addLangs(['en', 'tr']);
// Set the default language
this.translate.setDefaultLang('en');
this._translateService.setDefaultLang('en');
// Set the navigation translations
this.fuseTranslationLoader.loadTranslations(navigationEnglish, navigationTurkish);
this._fuseTranslationLoaderService.loadTranslations(navigationEnglish, navigationTurkish);
// Use a language
this.translate.use('en');
this._translateService.use('en');
// Add is-mobile class to the body if the platform is mobile
if ( this._platform.ANDROID || this._platform.IOS )
{
this.document.body.className += ' is-mobile';
}
// Set the private defaults
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Subscribe to config changes
this._fuseConfigService.config
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config) => {
this.fuseConfig = config;
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle sidebar open
*
* @param key
*/
toggleSidebarOpen(key): void
{
this._fuseSidebarService.getSidebar(key).toggleOpen();
}
}

View File

@@ -3,48 +3,25 @@ import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule, Routes } from '@angular/router';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { MatMomentDateModule } from '@angular/material-moment-adapter';
import { MatButtonModule, MatIconModule } from '@angular/material';
import { TranslateModule } from '@ngx-translate/core';
import 'hammerjs';
import { FuseModule } from '@fuse/fuse.module';
import { FuseSharedModule } from '@fuse/shared.module';
import { FuseSidebarModule, FuseThemeOptionsModule } from '@fuse/components';
import { fuseConfig } from './fuse-config';
import { fuseConfig } from 'app/fuse-config';
import { AppComponent } from './app.component';
import { FuseFakeDbService } from './fuse-fake-db/fuse-fake-db.service';
import { FuseMainModule } from './main/main.module';
import { AppStoreModule } from './store/store.module';
import { AppComponent } from 'app/app.component';
import { LayoutModule } from 'app/layout/layout.module';
import { SampleModule } from 'app/main/sample/sample.module';
const appRoutes: Routes = [
{
path : 'apps',
loadChildren: './main/content/apps/apps.module#FuseAppsModule'
},
{
path : 'pages',
loadChildren: './main/content/pages/pages.module#FusePagesModule'
},
{
path : 'ui',
loadChildren: './main/content/ui/ui.module#FuseUIModule'
},
{
path : 'services',
loadChildren: './main/content/services/services.module#FuseServicesModule'
},
{
path : 'components',
loadChildren: './main/content/components/components.module#FuseComponentsModule'
},
{
path : 'components-third-party',
loadChildren: './main/content/components-third-party/components-third-party.module#FuseComponentsThirdPartyModule'
},
{
path : '**',
redirectTo: 'apps/dashboards/analytics'
redirectTo: 'sample'
}
];
@@ -59,17 +36,23 @@ const appRoutes: Routes = [
RouterModule.forRoot(appRoutes),
TranslateModule.forRoot(),
InMemoryWebApiModule.forRoot(FuseFakeDbService, {
delay : 0,
passThruUnknownUrl: true
}),
// Fuse Main and Shared modules
// Material moment date module
MatMomentDateModule,
// Material
MatButtonModule,
MatIconModule,
// Fuse modules
FuseModule.forRoot(fuseConfig),
FuseSharedModule,
FuseSidebarModule,
FuseThemeOptionsModule,
AppStoreModule,
FuseMainModule
// App modules
LayoutModule,
SampleModule
],
bootstrap : [
AppComponent

View File

@@ -1,23 +0,0 @@
/**
* Default Fuse Configuration
*
* You can edit these options to change the default options. All these options also can be changed per component
* basis. See `app/main/content/pages/authentication/login/login.component.ts` constructor method to learn more
* about changing these options per component basis.
*/
export const fuseConfig = {
layout : {
navigation : 'left', // 'right', 'left', 'top', 'none'
navigationFolded: false, // true, false
toolbar : 'below', // 'above', 'below', 'none'
footer : 'below', // 'above', 'below', 'none'
mode : 'fullwidth' // 'boxed', 'fullwidth'
},
colorClasses : {
toolbar: 'mat-white-500-bg',
navbar : 'mat-fuse-dark-700-bg',
footer : 'mat-fuse-dark-900-bg'
},
customScrollbars: true,
routerAnimation : 'fadeIn' // fadeIn, slideUp, slideDown, slideRight, slideLeft, none
};

View File

@@ -0,0 +1,38 @@
import { FuseConfig } from '@fuse/types';
/**
* Default Fuse Configuration
*
* You can edit these options to change the default options. All these options also can be changed per component
* basis. See `app/main/pages/authentication/login/login.component.ts` constructor method to learn more
* about changing these options per component basis.
*/
export const fuseConfig: FuseConfig = {
layout : {
style : 'vertical-layout-1',
width : 'fullwidth',
navbar : {
background: 'mat-fuse-dark-700-bg',
folded : false,
hidden : false,
position : 'left',
variant : 'vertical-style-1'
},
toolbar : {
background: 'mat-white-500-bg',
hidden : false,
position : 'below-static'
},
footer : {
background: 'mat-fuse-dark-900-bg',
hidden : false,
position : 'below-fixed'
},
sidepanel: {
hidden : false,
position: 'right'
}
},
customScrollbars: true
};

View File

@@ -1,773 +0,0 @@
export class AcademyFakeDb
{
public static courses = [
{
'id' : '15459251a6d6b397565',
'title' : 'Basics of Angular',
'slug' : 'basics-of-angular',
'category': 'web',
'length' : 30,
'updated' : 'Jun 28, 2017'
},
{
'id' : '154588a0864d2881124',
'title' : 'Basics of TypeScript',
'slug' : 'basics-of-typeScript',
'category': 'web',
'length' : 60,
'updated' : 'Nov 01, 2017'
},
{
'id' : '15453ba60d3baa5daaf',
'title' : 'Android N: Quick Settings',
'slug' : 'android-n-quick-settings',
'category': 'android',
'length' : 120,
'updated' : 'Jun 28, 2017'
},
{
'id' : '15453a06c08fb021776',
'title' : 'Keep Sensitive Data Safe and Private',
'slug' : 'keep-sensitive-data-safe-and-private',
'category': 'android',
'length' : 45,
'updated' : 'Jun 28, 2017'
},
{
'id' : '15427f4c1b7f3953234',
'title' : 'Building a gRPC Service with Java',
'slug' : 'building-a-grpc-service-with-java',
'category': 'cloud',
'length' : 30,
'updated' : 'Jun 28, 2017'
},
{
'id' : '1542d75d929a603125',
'title' : 'Build a PWA Using Workbox',
'slug' : 'build-a-pwa-using-workbox',
'category': 'web',
'length' : 120,
'updated' : 'Jun 28, 2017'
},
{
'id' : '1543ee3a5b43e0f9f45',
'title' : 'Build an App for the Google Assistant with Firebase and Dialogflow',
'slug' : 'build-an-app-for-the-google-assistant-with-firebase-and-dialogflow',
'category': 'firebase',
'length' : 30,
'updated' : 'Jun 28, 2017'
},
{
'id' : '1543cc4515df3146112',
'title' : 'Cloud Functions for Firebase',
'slug' : 'cloud-functions-for-firebase',
'category': 'firebase',
'length' : 45,
'updated' : 'Jun 28, 2017'
},
{
'id' : '154398a4770d7aaf9a2',
'title' : 'Manage Your Pivotal Cloud Foundry App\'s Using Apigee Edge',
'slug' : 'manage-your-pivotal-cloud-foundry-apps-using-apigee-Edge',
'category': 'cloud',
'length' : 90,
'updated' : 'Jun 28, 2017'
},
{
'id' : '15438351f87dcd68567',
'title' : 'Building Beautiful UIs with Flutter',
'your' : 'building-beautiful-uis-with-flutter',
'category': 'web',
'length' : 90,
'updated' : 'Jun 28, 2017'
},
{
'id' : '1544e43dcdae6ebf876',
'title' : 'Cloud Firestore',
'slug' : 'cloud-firestore',
'category': 'firebase',
'length' : 90,
'updated' : 'Jun 28, 2017'
},
{
'id' : '1541ca7af66da284177',
'title' : 'Customize Network Topology with Subnetworks',
'slug' : 'customize-network-topology-with-subnetworks',
'category': 'web',
'length' : 45,
'updated' : 'Jun 28, 2017'
},
{
'id' : '154297167e781781745',
'title' : 'Looking at Campaign Finance with BigQuery',
'slug' : 'looking-at-campaign-finance-with-bigquery',
'category': 'cloud',
'length' : 60,
'updated' : 'Jun 28, 2017'
},
{
'id' : '154537435d5b32bf11a',
'title' : 'Firebase Android',
'slug' : 'firebase-android',
'category': 'android',
'length' : 45,
'updated' : 'Jun 28, 2017'
},
{
'id' : '154204e45a59b168453',
'title' : 'Simulating a Thread Network Using OpenThread',
'slug' : 'simulating-a-thread-network-using-openthread',
'category': 'web',
'length' : 45,
'updated' : 'Jun 28, 2017'
},
{
'id' : '1541dd1e05dfc439216',
'title' : 'Your First Progressive Web App',
'slug' : 'your-first-progressive-web-app',
'category': 'web',
'length' : 30,
'updated' : 'Jun 28, 2017'
},
{
'id' : '1532dfc67e704e48515',
'title' : 'Launch Cloud Datalab',
'slug' : 'launch-cloud-datalab',
'category': 'cloud',
'length' : 60,
'updated' : 'Jun 28, 2017'
},
{
'id' : '1542e43dfaae6ebf226',
'title' : 'Personalize Your iOS App with Firebase User Management',
'slug' : 'personalize-your-ios-app-with-firebase-user-management',
'category': 'firebase',
'length' : 90,
'updated' : 'Jun 28, 2017'
}
];
public static categories = [
{
'id' : 0,
'value': 'web',
'label': 'Web'
},
{
'id' : 1,
'value': 'firebase',
'label': 'Firebase'
},
{
'id' : 2,
'value': 'cloud',
'label': 'Cloud'
},
{
'id' : 3,
'value': 'android',
'label': 'Android'
}
];
private static demoSteps = [
{
'title' : 'Introduction',
'content': '<h1>Step 1 - Introduction</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'Get the sample code',
'content': '<h1>Step 2 - Get the sample code</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'Create a Firebase project and Set up your app',
'content': '<h1>Step 3 - Create a Firebase project and Set up your app</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'Install the Firebase Command Line Interface',
'content': '<h1>Step 4 - Install the Firebase Command Line Interface</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'Deploy and run the web app',
'content': '<h1>Step 5 - Deploy and run the web app</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'The Functions Directory',
'content': '<h1>Step 6 - The Functions Directory</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'Import the Cloud Functions and Firebase Admin modules',
'content': '<h1>Step 7 - Import the Cloud Functions and Firebase Admin modules</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'Welcome New Users',
'content': '<h1>Step 8 - Welcome New Users</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'Images moderation',
'content': '<h1>Step 9 - Images moderation</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'New Message Notifications',
'content': '<h1>Step 10 - New Message Notifications</h1>' +
'<br>' +
'This is an example step of the course. You can put anything in here from example codes to videos.' +
'<br><br>' +
'To install the CLI you need to have installed <b>npm</b> which typically comes with <b>NodeJS</b>.' +
'To install or upgrade the CLI run the following <b>npm</b> command:' +
'<br><br>' +
'<code>npm -g install @angular/cli</code>' +
'<br><br>' +
'To verify that the CLI has been installed correctly, open a console and run:' +
'<br><br>' +
'<code>ng version</code>' +
'<br><br>' +
'<h2>Install dependencies</h2>' +
'<br>' +
'To moderate the images we\'ll need a few Node.js packages:' +
'<br><br>' +
'<ul>' +
'<li>' +
'The Google Cloud Vision Client Library for Node.js: @google-cloud/vision to run the image through the Cloud Vision API to detect inappropriate images.' +
'</li>' +
'<br>' +
'<li>' +
'The Google Cloud Storage Client Library for Node.js: @google-cloud/storage to download and upload the images from Cloud Storage.' +
'</li>' +
'<br>' +
'<li>' +
'A Node.js library allowing us to run processes: child-process-promise to run ImageMagick since the ImageMagick command-line tool comes pre-installed on all Functions instances.' +
'</li>' +
'</ul>' +
'<br>' +
'To install these three packages into your Cloud Functions app, run the following npm install --save command. Make sure that you do this from the functions directory.' +
'<br><br>' +
'<code>npm install --save @google-cloud/vision @google-cloud/storage child-process-promise</code>' +
'<br><br>' +
'This will install the three packages locally and add them as declared dependencies in your package.js file.'
},
{
'title' : 'Congratulations!',
'content': '<h1>Step 11 - Congratulations!</h1>' +
'<br>' +
'You\'ve built a full-fidelity, offline-capable progressive web app by leveraging the power of reusable Web Components and Firebase. Why bother with a native app when you know how to do all that?!'
}
];
public static course = [
{
'id' : '15459251a6d6b397565',
'title' : 'Basics of Angular',
'slug' : 'basics-of-angular',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'web',
'length' : 30,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '154588a0864d2881124',
'title' : 'Basics of TypeScript',
'slug' : 'basics-of-typeScript',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'web',
'length' : 60,
'totalSteps' : 11,
'updated' : 'Nov 01, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '15453ba60d3baa5daaf',
'title' : 'Android N: Quick Settings',
'slug' : 'android-n-quick-settings',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'android',
'length' : 120,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '15453a06c08fb021776',
'title' : 'Keep Sensitive Data Safe and Private',
'slug' : 'keep-sensitive-data-safe-and-private',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'android',
'length' : 45,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '15427f4c1b7f3953234',
'title' : 'Building a gRPC Service with Java',
'slug' : 'building-a-grpc-service-with-java',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'cloud',
'length' : 30,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '1542d75d929a603125',
'title' : 'Build a PWA Using Workbox',
'slug' : 'build-a-pwa-using-workbox',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'web',
'length' : 120,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '1543ee3a5b43e0f9f45',
'title' : 'Build an App for the Google Assistant with Firebase and Dialogflow',
'slug' : 'build-an-app-for-the-google-assistant-with-firebase-and-dialogflow',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'firebase',
'length' : 30,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '1543cc4515df3146112',
'title' : 'Cloud Functions for Firebase',
'slug' : 'cloud-functions-for-firebase',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'firebase',
'length' : 45,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '154398a4770d7aaf9a2',
'title' : 'Manage Your Pivotal Cloud Foundry App\'s Using Apigee Edge',
'slug' : 'manage-your-pivotal-cloud-foundry-apps-using-apigee-Edge',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'cloud',
'length' : 90,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '15438351f87dcd68567',
'title' : 'Building Beautiful UIs with Flutter',
'your' : 'building-beautiful-uis-with-flutter',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'web',
'length' : 90,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '1544e43dcdae6ebf876',
'title' : 'Cloud Firestore',
'slug' : 'cloud-firestore',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'firebase',
'length' : 90,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '1541ca7af66da284177',
'title' : 'Customize Network Topology with Subnetworks',
'slug' : 'customize-network-topology-with-subnetworks',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'web',
'length' : 45,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '154297167e781781745',
'title' : 'Looking at Campaign Finance with BigQuery',
'slug' : 'looking-at-campaign-finance-with-bigquery',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'cloud',
'length' : 60,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '154537435d5b32bf11a',
'title' : 'Firebase Android',
'slug' : 'firebase-android',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'android',
'length' : 45,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '154204e45a59b168453',
'title' : 'Simulating a Thread Network Using OpenThread',
'slug' : 'simulating-a-thread-network-using-openthread',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'web',
'length' : 45,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '1541dd1e05dfc439216',
'title' : 'Your First Progressive Web App',
'slug' : 'your-first-progressive-web-app',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'web',
'length' : 30,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '1532dfc67e704e48515',
'title' : 'Launch Cloud Datalab',
'slug' : 'launch-cloud-datalab',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'cloud',
'length' : 60,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
},
{
'id' : '1542e43dfaae6ebf226',
'title' : 'Personalize Your iOS App with Firebase User Management',
'slug' : 'personalize-your-ios-app-with-firebase-user-management',
'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'category' : 'firebase',
'length' : 90,
'totalSteps' : 11,
'updated' : 'Jun 28, 2017',
'steps' : AcademyFakeDb.demoSteps
}
];
}

Some files were not shown because too many files have changed in this diff Show More