Compare commits

..

226 Commits

Author SHA1 Message Date
sercan 82244fd40e Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 15:50:30 +03:00
sercan c5433cb917 Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 15:49:55 +03:00
sercan b6a3023ebd Increase the version number 2021-08-31 15:49:10 +03:00
sercan 693e44dbaf (QuickChat) Fixed: Overflowing issue 2021-08-31 15:47:35 +03:00
sercan cf5f13d78e Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 10:18:08 +03:00
sercan 206ef711a4 (QuickChat) Don't use types from outside 2021-08-31 10:17:57 +03:00
sercan ef85907d1b Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 10:17:19 +03:00
sercan 7e0cc1249e (QuickChat) Don't use types from outside 2021-08-31 10:17:06 +03:00
sercan 138a43da59 (QuickChat) Don't use types from outside 2021-08-31 10:16:13 +03:00
sercan cafc3188cf Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 10:07:33 +03:00
sercan fe7e2514a6 Updated dependencies, increased the version number & updated the changelog 2021-08-31 09:43:10 +03:00
sercan b8f0d9f0b2 (QuickChat) Added the Quick Chat bar 2021-08-31 09:21:28 +03:00
sercan 2a4a392153 (layout) Separated the Settings drawer from the layout component 2021-08-31 09:20:55 +03:00
sercan c2fa88f4d4 (fuse/drawer) Fixed: Final opacity of the overlay is not permanent due to player being destroyed right after the animation 2021-08-17 22:05:13 +03:00
sercan 6381362326 Merge remote-tracking branch 'origin/demo' into starter 2021-08-12 17:05:40 +03:00
sercan 67d25012ec Increased the version number & updated the changelog 2021-08-12 17:04:57 +03:00
sercan e1942f46fd Merge remote-tracking branch 'origin/demo' into starter 2021-08-12 16:57:58 +03:00
sercan 6de6a07778 Decreased budget sizes since new Fuse is a lot smaller compared to the one with the old design 2021-08-12 13:57:29 +03:00
sercan e7ab0ea13f (apps/mailbox) Fixed: Compose dialog doesn't work correctly on small height resolutions
(apps/mailbox) Style improvements
2021-08-12 13:45:34 +03:00
sercan 4b686d86cc (@fuse/overrides) Fixed: Quill editor is not styled correctly by default 2021-08-12 13:42:59 +03:00
sercan 190395f14b (@fuse/theming) Better structuring on the themes.scss file
(@fuse) Disabled Angular Material 'theme' sanity check since we use 'all-component-themes' without a color map
2021-08-12 13:17:53 +03:00
sercan 4a9b8ee91e (dependencies) Updated Angular, Angular Material and various other dependencies 2021-08-12 11:32:04 +03:00
sercan 488fe8a1eb (ui/page-layouts) Fixed: Demo layout navigation appearance is not correct 2021-08-11 22:25:49 +03:00
sercan db9a2a2433 (dependencies) Updated Angular, Angular Material and various other dependencies 2021-08-11 22:25:15 +03:00
sercan 3dda8479cf (tailwindcss) Removed old jsdoc from the config file 2021-08-08 22:27:24 +03:00
sercan 7c402670a1 (docs/confirmation) Updated the docs of the confirmation dialog 2021-08-04 10:10:07 +03:00
sercan f6bf0fb5d3 (fuse/confirmation) Fixed: Dialog size cannot be updated using dialogRef's "updateSize" method 2021-08-04 09:54:35 +03:00
sercan d44c004d01 Removed empty "styles" from component decorators 2021-08-04 09:46:36 +03:00
sercan ce656430c8 Merge remote-tracking branch 'origin/demo' into starter 2021-07-29 10:40:12 +03:00
sercan 4ec40271c5 Updated the package.json version number 2021-07-29 10:39:16 +03:00
sercan c93b1ce232 Merge remote-tracking branch 'origin/demo' into starter 2021-07-29 10:23:27 +03:00
sercan b874b37db2 Merge remote-tracking branch 'origin/demo' into starter 2021-07-29 10:21:30 +03:00
sercan b21cdf1655 Updated the changelog and increased the version number 2021-07-29 10:19:28 +03:00
sercan 6fff259fe3 (dependencies) Updated Angular, Angular Material and various other dependencies 2021-07-29 10:15:13 +03:00
sercan 8fcb0aea03 (docs) Updated the multi language guide 2021-07-29 10:14:52 +03:00
sercan 2c90770d9b (index) Updated the title, description and keywords 2021-07-23 22:29:45 +03:00
sercan 1581ea74cc (dashboards/finance) Added finance dashboard
(dashboards/crypto) Added crypto dashboard
2021-07-23 22:23:42 +03:00
sercan 569809aabb (dashboards/project) Small tweaks 2021-07-23 22:23:02 +03:00
sercan dde9333120 (dashboards/project) Small tweaks on the header 2021-07-23 21:30:51 +03:00
sercan 10ec1790ca (dashboards/project) Module import order 2021-07-21 12:22:23 +03:00
sercan a2ff55d4c1 (dashboards/project) Light header on light themes, small adjustments in the project selector 2021-07-21 12:22:02 +03:00
sercan 966e2db743 (apps/contacts) Small adjustments for better consistency
(apps/ecommerce/inventory) Small adjustments for better consistency
2021-07-21 11:36:43 +03:00
sercan 4287361a09 Merge remote-tracking branch 'origin/demo' into starter 2021-07-16 20:49:22 +03:00
sercan 4f19eb6171 Merge remote-tracking branch 'origin/demo' into starter 2021-07-16 20:48:48 +03:00
sercan 49cccde93b Increased the version number & updated the changelog 2021-07-16 20:47:50 +03:00
sercan 952b64297b (fuse/confirmation) Fixed: Confirmation dialog colors are not correct for the Dark mode 2021-07-16 20:45:35 +03:00
sercan 9b3ff6a724 Updated changelog 2021-07-16 12:56:35 +03:00
sercan cd13f9d9a4 Merge remote-tracking branch 'origin/demo' into starter 2021-07-16 12:38:44 +03:00
sercan db8c52f093 Merge remote-tracking branch 'origin/demo' into starter 2021-07-16 12:36:58 +03:00
sercan 961b86c8cb (apps/contacts) Added confirmation to the "Delete contact" action using FuseConfirmationService
(apps/ecommerce/inventory) Added confirmation to the "Delete product" action using FuseConfirmationService
(apps/scrumboard) Added confirmation to the "Delete list" action using FuseConfirmationService
(apps/tasks) Added confirmation to the "Delete task" action using FuseConfirmationService
2021-07-16 12:28:43 +03:00
sercan 8b977c0eeb Updated Angular, Angular Material and couple other dependencies & increased the version number 2021-07-16 12:00:46 +03:00
sercan 35e56cd6af (fuse/confirmation) Small tweak on the docs 2021-07-12 12:39:52 +03:00
sercan c04550b887 (ui/confirmation-dialog) Created a separate page for FuseConfirmationService and moved the example configurator to there for better visibility 2021-07-12 12:37:47 +03:00
sercan 747b4f44c8 (ui/advanced-search) Moved the routing information into the module file 2021-07-12 12:37:06 +03:00
sercan c4914c80b3 (fuse/confirmation) Small fix on the docs 2021-07-10 22:11:39 +03:00
sercan 178d09597b (fuse/confirmation) First iteration of the FuseConfirmationService 2021-07-10 22:03:58 +03:00
sercan d206c55e6e (ui/fuse-components) Renamed the component reference 2021-07-09 11:49:21 +03:00
sercan 9abe887df1 (docs) Moved Fuse Components and Other Components into UI for better visibility and better categorization 2021-07-09 11:30:00 +03:00
sercan b87fdc407c (Navigation) Casing fixes on navigation data 2021-07-09 11:12:52 +03:00
sercan 58440b63a7 (ui/material-components) Renamed the component 2021-07-09 11:12:07 +03:00
sercan 63f6edee9a (tailwindcss) Fixed: Ordered lists with "s" modifier causes builder to throw errors 2021-07-05 23:13:38 +03:00
sercan 2189232273 Merge remote-tracking branch 'origin/demo' into starter 2021-07-02 23:11:01 +03:00
sercan ca8e422b21 (app.module) Alphabetical order on Router's extra options 2021-07-02 23:10:41 +03:00
sercan 36b89e75db Update package name 2021-07-02 23:05:27 +03:00
sercan 8c2ff122b2 Merge remote-tracking branch 'origin/demo' into starter 2021-07-02 23:01:10 +03:00
sercan cc703081ca (dashboards/analytics) Removed unused chart options declarations 2021-07-02 22:59:47 +03:00
sercan 038c74df50 (transloco) Defined a fallback language 2021-07-02 22:50:25 +03:00
sercan 9abbf5fec2 (karma.conf.js) Updated the default path 2021-07-02 22:50:19 +03:00
sercan 0ebc16ec05 (package.json) Removed "e2e" script since we don't provide e2e testing environment by default 2021-07-02 22:50:10 +03:00
sercan b2cb512b0e Merge remote-tracking branch 'origin/demo' into starter 2021-07-01 14:29:26 +03:00
sercan 21652570c2 Merge remote-tracking branch 'origin/demo' into starter 2021-07-01 14:28:05 +03:00
sercan 56c4eb0356 Updated changelog 2021-07-01 14:26:49 +03:00
sercan a8a4f2b18d Updated changelog and increased the version number 2021-07-01 13:31:41 +03:00
sercan 36c8727616 (layout/settings-drawer) Fixed: Issues on small screen devices 2021-07-01 12:55:02 +03:00
sercan 5ea37aed99 (layouts) Fixed: Header buttons are not fitting on certain layouts
(layouts) Hide the "fuse-fullscreen" button on smaller devices since they don't support fullscreen
2021-07-01 12:50:34 +03:00
sercan cbce71f155 (apps/help-center) Fixed: Small dark mode issues 2021-07-01 12:37:27 +03:00
sercan ed47050232 (apps/ecommerce/inventory) Fixed: Tags selector border colors are not correct on dark mode 2021-07-01 12:37:09 +03:00
sercan c86a538d41 (Angular Material) Apply rounded styles by default 2021-07-01 12:23:49 +03:00
sercan f369206ef8 (layout/common) Explicitly define the overlay position strategy properties
(apps/contacts) Explicitly define the overlay position strategy properties
(apps/mailbox) Explicitly define the overlay position strategy properties
(apps/tasks) Explicitly define the overlay position strategy properties
2021-06-25 22:57:14 +03:00
sercan 1659d4babd (tailwindcss) Fixed: Dark mode classes are not being purged correctly due to the wrong safelist entry 2021-06-25 12:21:34 +03:00
sercan fa37f99d33 (docs) Use alternative spacing since 12s are removed 2021-06-25 11:09:30 +03:00
sercan 331968ac5b (tailwindcss) Breaking: Removed 5, 6 & 12 fractional spacing values since they are not used in Demo by default and they are mostly not needed because of Flex and Grid. If you happen to use them, you can manually add them back. 2021-06-25 11:08:56 +03:00
sercan 27274c84d6 (@fuse/navigation) Fixed: Vertical navigation blocks scroll if it's destroyed while in 'over' mode and opened 2021-06-24 18:04:19 +03:00
sercan 4b8a101a3e (transloco) Fixed: Language files cannot be loaded if using a base href other than "/" 2021-06-24 10:54:03 +03:00
sercan 8080a85d40 (fuse-drawer) Fixed: Memory leak due to the animation player, thanks to Vadym Pidoplichko for coming up the issue and the solution. 2021-06-24 10:29:34 +03:00
sercan 36784c405f (package.json) Added "description" and "author" fields 2021-06-21 18:56:15 +03:00
sercan 8f4f7886d5 (fuse/navigation) Moved *ngIf directives into their own "ng-container" containers 2021-06-20 21:59:24 +03:00
sercan d693a08136 (general) Tweaked the "Angular Material components" navigation item title 2021-06-20 21:30:10 +03:00
sercan 0c0ef40de3 (docs) Added docs about navigation tooltip 2021-06-20 21:29:37 +03:00
sercan f4d737d3a3 (fuse/navigation) Added "tooltip" property to show tooltips on navigation items using MatTooltip 2021-06-20 21:28:47 +03:00
sercan 90891eb201 Merge remote-tracking branch 'origin/demo' into starter 2021-06-15 08:36:32 +03:00
sercan 47d9ddb08c Updated changelog 2021-06-15 08:32:13 +03:00
sercan 4da3612d22 Updated changelog and increased the version number 2021-06-14 14:29:58 +03:00
sercan 403a949d4a (apps/ecommerce/inventory) Small mobile experience tweaks 2021-06-14 12:25:04 +03:00
sercan 5b78a68116 (apps/ecommerce/inventory) Better image containment on row details 2021-06-14 12:22:51 +03:00
sercan 214116e10d (apps/ecommerce/inventory) Replaced the mat-table with a custom grid for better mobile experience and better performance, improved the mobile experience 2021-06-14 12:03:17 +03:00
sercan 5962c80e8d (docs) Added "target" docs to navigation 2021-06-14 08:53:01 +03:00
sercan ff086b1ed0 (fuse/navigation) Added "target" for setting the target attribute on outgoing links. Thanks @danielehrhardt for the original PR, closes #154 2021-06-14 08:52:29 +03:00
sercan efdfa6418a (core) Go back to the 'classy' layout by default 2021-06-10 14:56:21 +03:00
sercan c1c9904b9d (layouts/common/search) Improved the autocomplete design 2021-06-10 14:56:00 +03:00
sercan 39650d3cc9 (layouts) Use navigation service data for horizontal navigation 2021-06-10 10:13:43 +03:00
sercan bafa9adc01 (ui/page-layouts) Added tabbed version of Simple Fullwidth page layout 2021-06-09 11:41:39 +03:00
sercan bb9023f9df (fuse/navigation) Fixed: First children of collapsable items don't have proper spacing at the top 2021-06-09 10:32:26 +03:00
sercan 66096718e0 (docs) Updated the docs to reflect the latest changes 2021-06-09 10:22:18 +03:00
sercan 945d0a2240 (fuse/fullscreen) Added [tooltip] & [iconTpl] inputs for customizing the Fullscreen trigger button 2021-06-08 18:29:49 +03:00
sercan 9005f08ac7 (app.resolver) Use services to request the initial data
(layouts) Common components of layouts now requests their data directly from their service rather than getting it from route data
(core) New navigation service to request and store the navigation data
2021-06-08 17:29:45 +03:00
sercan 89f5a4ec69 (core/user) Renamed user.model to user.types for better consistency 2021-06-07 12:18:39 +03:00
sercan ca7b4c7e5d (ui/advanced-search) Removed unused MatIconModule import 2021-06-07 11:41:29 +03:00
sercan bb57ec2324 (ui/advanced-search) Added an example search form that uses query parameters for Advanced Search forms 2021-06-07 11:40:33 +03:00
sercan 11ad2c89df (apps/file-manager) Added support for nested folder views 2021-06-05 13:57:39 +03:00
sercan fc1e7b02b0 (data/navigation) Fixed the "Invoice" icon, added missing dashboard links to the "futuristicNavigation" data 2021-06-04 22:28:53 +03:00
sercan 446bfe4139 (ui/angular-material) Added a component list that redirects to the related page of the official docs 2021-06-04 22:21:47 +03:00
sercan 6c7201b77a Merge remote-tracking branch 'origin/demo' into starter 2021-06-03 13:28:30 +03:00
sercan 2c5cd60c0a (general) Updated version numbers and changelog 2021-06-03 13:25:07 +03:00
sercan decb238f73 (dependencies) Updated various dependencies to their latest versions
(fuse/highlight) Updated the import path
2021-06-03 13:18:05 +03:00
sercan ab3ad4fd2f (apps/scrumboard) Dark mode fixes and tweaks 2021-06-03 11:27:51 +03:00
sercan c2dd77d7a3 (angular.json) Removed "e2e" entry, fixed the styles file path for "test" 2021-06-02 12:45:12 +03:00
sercan a78b087a68 (apps/scrumboard) New version of the Scrumboard app 2021-06-02 11:42:22 +03:00
sercan 84d40427a1 (apps/ecommerce) Small tweaks
(apps/mailbox) Small tweaks
2021-06-02 11:41:47 +03:00
sercan f295fd9061 (fuse/autogrow) BREAKING: Removed fuseAutogrow in favor of matTextareaAutosize since all of its problems solved, use [matTextareaAutosize] without any vertical padding on the textarea itself. 2021-05-31 17:14:03 +03:00
sercan b98cfc1d37 (Angular Material) Increased default MatDialog border radius to 16px for better consistency 2021-05-31 16:12:08 +03:00
sercan 0ba5677c01 (Dependencies) Updated Angular & Angular Material to v12.0.2 2021-05-31 10:49:51 +03:00
sercan 90f869e7b9 Merge remote-tracking branch 'origin/demo' into starter 2021-05-31 10:46:26 +03:00
sercan 96ef1281ae (Angular Material) Fixed: Density setting is not working for dark themes 2021-05-31 10:45:29 +03:00
sercan 2d2db97416 Merge remote-tracking branch 'origin/demo' into starter 2021-05-24 14:33:12 +03:00
sercan 466bf50de4 Increased the version number
Updated the changelog
2021-05-24 14:32:45 +03:00
sercan 5daa2260e6 Merge remote-tracking branch 'origin/demo' into starter 2021-05-24 14:29:35 +03:00
sercan 95bc7dc4db (apps/ecommerce/inventory) Performance improvements, decreased the mockApi delay 2021-05-24 09:22:18 +03:00
sercan af984fcba1 Merge remote-tracking branch 'origin/demo' into starter 2021-05-23 07:13:28 +03:00
sercan cfca19dc68 (mockApi) Removed typings from data files 2021-05-23 07:11:58 +03:00
sercan a0c20f8d59 (pages/settings) Fixed: Settings container component width is not filling the container 2021-05-22 21:14:42 +03:00
sercan 97d3662417 Merge remote-tracking branch 'origin/demo' into starter 2021-05-21 12:17:06 +03:00
sercan df9f2256cd Increased the version number
Updated the changelog
2021-05-21 12:02:05 +03:00
sercan 6bcd04b799 (eslint) Fixed linting issues 2021-05-21 11:48:42 +03:00
sercan 3c0e326113 (dependencies) Updated various packages to their latest versions 2021-05-21 11:40:50 +03:00
sercan c7158377f7 Revert "(dependencies) Updated Angular & Angular Material to v12.0.1"
This reverts commit 105575d4
2021-05-20 22:46:40 +03:00
sercan 105575d40e (dependencies) Updated Angular & Angular Material to v12.0.1 2021-05-20 22:23:25 +03:00
sercan f470313d72 (pages/activities) Couple data tweaks 2021-05-20 16:51:40 +03:00
sercan 60e9c65505 (pages/activities) Couple data and style tweaks 2021-05-20 16:48:38 +03:00
sercan 3b727fe859 (pages/activities) Added Activities page (timeline) 2021-05-20 15:30:14 +03:00
sercan 4694bb401d (layout/common) Added trackBy functions to ngFor loops in common components 2021-05-19 22:43:16 +03:00
sercan ee86dc6245 (tailwind) Use TAILWIND_MODE environment variable to activate purge on build 2021-05-19 14:45:30 +03:00
sercan 3d09241c64 (fuse/animations) Fixed: Barrel exports
(FuseMasonry) Fixed: Barrel exports
(FuseUtilsService) Fixed: Barrel exports
(FuseValidators) Fixed: Barrel exports
2021-05-19 14:37:38 +03:00
sercan e499c06884 (apps/mailbox) Small tweak on resolver's comment 2021-05-19 13:38:10 +03:00
sercan eca618c95b (FuseNavigation) Fixed: If [routerLinkActiveOptions] is "undefined" initially, router throws an error and stops working 2021-05-19 13:37:37 +03:00
sercan 8524df013a (fuse/tailwind) Fixed: Barrel exports are wrong for FuseTailwindService 2021-05-19 10:57:40 +03:00
sercan d897a244c8 Merge remote-tracking branch 'origin/demo' into starter 2021-05-18 16:25:53 +03:00
sercan e4df408abe (FuseNavigation) Added support for new "isActiveMatchOptions" for Basic navigation items; https://github.com/angular/angular/pull/40303
(docs) Updated FuseNavigation documentation
2021-05-18 16:09:31 +03:00
sercan fd859a8663 (comments) Fixed: The word 'data' replaced with 'mock-api' by mistake in the past causing a lot of comments to make no sense 2021-05-18 15:18:44 +03:00
sercan 59960af7a5 (MultiLang) Added multi language support using @ngneat/transloco
(docs) Added docs about multi language
2021-05-18 15:11:57 +03:00
sercan 74c4dc2ad8 (app.module.ts) Small tweaks on comments 2021-05-18 14:34:46 +03:00
sercan f76f38b812 (FuseVerticalNavigation) Added missing return types 2021-05-18 14:12:38 +03:00
sercan 5c40e99518 (FuseNavigation) Use the generic return type for "getComponent"
(docs) Updated FuseNavigation docs
2021-05-18 13:10:39 +03:00
sercan c1ca701e92 (FuseNavigation) Added a generic return type for "getComponent" method on FuseNavigationService 2021-05-18 13:08:02 +03:00
sercan e00dda98bc (core) Separated the "auth" and "icon registry" to their own modules to keep the CoreModule simple 2021-05-18 11:44:42 +03:00
sercan 9edc62f703 (eslint) Activate explicit return types on functions and methods 2021-05-18 11:42:36 +03:00
sercan f9c8e16778 (fuse/mock-api) Added a return type to the factory function 2021-05-17 15:21:08 +03:00
sercan 4a30e3482c (eslint) Removed e2e tsconfig path as there is no default e2e solution included into Angular since v12.0.0 2021-05-16 16:22:40 +03:00
sercan 837f444cc9 (overrides/angular-material) Use @apply whenever it's possible 2021-05-16 15:55:17 +03:00
sercan d0876eb80c (overrides) Change the text and arrow color of mat-select on focus when it's used as a prefix or suffix in mat-form-field 2021-05-16 15:54:55 +03:00
sercan 3395e7f0bc (docs) Updated docs 2021-05-15 13:44:13 +03:00
sercan d146a92c79 Merge remote-tracking branch 'origin/demo' into starter 2021-05-15 13:18:13 +03:00
sercan ee1caef303 Increased the version number
(changelog) Updated changelog
2021-05-14 17:44:18 +03:00
sercan cad136c0e5 (dependencies) Updated packages 2021-05-14 17:26:28 +03:00
sercan d159ae1458 (linting) Migrated over to the ESLint 2021-05-14 17:17:06 +03:00
sercan 0c5f5b9165 (global) "ng build" automatically builds into production mode by default 2021-05-14 14:15:54 +03:00
sercan f4ca06a9a8 (dependencies) Updated Angular Material to v12.0.0 2021-05-14 14:15:14 +03:00
sercan 6a86deaeec (dependencies) Updated Angular to v12.0.0 2021-05-14 12:12:24 +03:00
sercan 44e7401310 (pages/settings) Moved titles to the same line with sidebar toggle button + small alignment tweaks 2021-05-12 15:49:36 +03:00
sercan 6d1dee8d0d (3rdParty) Fixed: "_redirects" file must be in /src folder 2021-05-12 14:47:53 +03:00
sercan b8b3d1daab (3rdParty) Added a _redirects file for Netlify support 2021-05-12 14:28:16 +03:00
sercan c3ed184853 (pages/settings) Added a close button for the sidebar 2021-05-12 14:14:03 +03:00
sercan a9ada174b4 (routing) Use "corrected" behavior for relative link resolution (https://github.com/angular/angular/pull/22394) as it's the default value starting from Angular v11 (https://github.com/angular/angular/pull/25609) 2021-05-12 12:46:01 +03:00
sercan 57ba071fa5 (pages/settings) Fixed: Drawer shouldn't be closed on 'side' mode when changing the selected panel 2021-05-11 13:09:38 +03:00
sercan cc761d58e5 (pages/settings) Added new Settings page 2021-05-11 12:48:51 +03:00
sercan 5c3db88908 (apps/ecommerce) Tweaked hover color on inventory list for better consistency 2021-05-07 12:43:04 +03:00
sercan 039eef98ee (apps/chat) Tweaked hover colors on lists for better consistency 2021-05-07 12:40:02 +03:00
sercan 83e67e1286 (docs/changelog) Small visual improvement on changelog titles 2021-05-07 12:33:50 +03:00
sercan 2bea991ba3 (refactoring) Move *ngFor directives to their own <ng-container> elements. This is mostly a personal preference but it's a good habit to have as you cannot put more than one structural directive on a single element. This way our main repeated element is free of any common structural directives so we can use ours on them if needed. 2021-05-07 12:10:45 +03:00
sercan 7e430a269c (apps/tasks) Visual improvements
(apps/contacts) Tweaked the hover color on contacts list for better consistency
2021-05-07 11:54:47 +03:00
sercan 27b6858b76 Merge remote-tracking branch 'origin/demo' into starter 2021-05-06 17:12:50 +03:00
sercan 4ccce1b423 Increased the version number
(changelog) Updated the changelog
2021-05-06 17:11:12 +03:00
sercan f6b4ca0880 (apps/notes) Responsive adjustments 2021-05-06 17:05:56 +03:00
sercan 77014174e8 (apps/notes) New version of the Notes app 2021-05-06 17:01:14 +03:00
sercan 5ac7002a98 (fuse/masonry) Fixed: Masonry doesn't work with the data that comes from async pipe 2021-05-06 12:07:07 +03:00
sercan b0f1e1de95 (apps/mailbox) App title font size adjustment for better consistency 2021-05-05 17:52:10 +03:00
sercan cf01383358 (apps/mailbox) Use shadow on threads for better consistency 2021-05-05 17:27:39 +03:00
sercan e4442d683b (apps/tasks) Tweaked the hover color on task list for better consistency 2021-05-05 17:27:12 +03:00
sercan 623b43a94c (fuse/styles) Fixed: fuse-highlight doesn't have a margin around in Docs 2021-05-03 18:49:00 +03:00
sercan e7a1d386a6 (fuse/masonry) Added a new component (and its docs) for creating Masonry layouts 2021-05-03 18:47:57 +03:00
sercan b05763135e (apps/chat) Adjustments and optimizations for smaller devices 2021-04-30 19:55:37 +03:00
sercan fcfba4c9e4 Merge remote-tracking branch 'origin/demo' into starter 2021-04-30 19:40:30 +03:00
sercan 40894e0aa3 Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/app.routing.ts
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/apps/academy/academy.service.ts
#	src/app/modules/admin/apps/academy/details/details.component.html
#	src/app/modules/admin/apps/academy/list/list.component.html
#	src/app/modules/admin/apps/mailbox/list/list.component.ts
#	src/app/modules/admin/docs/changelog/changelog.ts
#	src/app/modules/admin/pages/pricing/modern/modern.component.html
#	src/app/modules/admin/pages/pricing/simple/simple.component.html
#	src/app/modules/admin/pages/pricing/single/single.component.html
#	src/app/modules/admin/pages/pricing/table/table.component.html
2021-04-30 19:40:07 +03:00
sercan 5dd60c816c (apps/chat) Small adjustments and tweaks for Dark mode 2021-04-30 19:39:02 +03:00
sercan 0ac967a945 Removed optional chaining operators to support Node v12
Set the version on .nvmrc to 12
2021-04-30 19:27:40 +03:00
sercan e3821da077 (apps/chat) New and improved Chat app 2021-04-30 19:18:09 +03:00
sercan ee48e11548 Increased the version number
(dependencies) Updated Angular, Angular Material and various other packages
(changelog) Updated the changelog
2021-04-30 19:07:53 +03:00
sercan 215546cc31 (apps/academy) Removed a misplaced import 2021-04-29 19:57:58 +03:00
sercan 072dbce6d4 (fuse/fullscreen) Added Fullscreen toggle component 2021-04-28 10:55:32 +03:00
sercan 5ffe0d0efa (pages/pricing) Improved the spacing of the CTA section on all pricing pages 2021-04-26 23:42:16 +03:00
sercan e90fb9e618 (apps/academy) Added missing trackBy functions to '*ngFor's 2021-04-26 16:41:21 +03:00
sercan 88e98d002d (apps/mailbox) Removed unused methods 2021-04-26 16:08:19 +03:00
sercan deeef323f9 (apps/academy) Better error handling on courses that are not exist 2021-04-26 15:59:44 +03:00
sercan 8dcf21cb1a Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 10:23:15 +03:00
sercan 284e282761 Remove quotes from .nvmrc version info 2021-04-26 10:23:08 +03:00
sercan d917f03883 Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 10:20:06 +03:00
sercan 52e234325f Added .nvmrc with required Node version 2021-04-26 10:19:52 +03:00
sercan 0f2ddbda83 Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/docs/changelog/changelog.ts
2021-04-26 09:56:44 +03:00
sercan fa0d74504b Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 09:56:29 +03:00
sercan 42e0864538 (dependencies) Updated Angular, Angular Material and various other packages
(changelog) Added changelog and updated the version number
2021-04-26 09:55:41 +03:00
sercan ad2b19a07a Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/app.routing.ts
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/apps/contacts/details/details.component.html
#	src/app/modules/admin/apps/file-manager/list/list.component.html
#	src/app/modules/admin/apps/file-manager/list/list.component.ts
#	src/app/modules/landing/home/home.component.html
2021-04-26 09:31:42 +03:00
sercan 6b6442b37f (apps/academy) Added page info in between the desktop navigation buttons 2021-04-26 09:29:59 +03:00
sercan bb0efade72 (apps/academy) New version of the Academy app 2021-04-25 23:23:23 +03:00
sercan 63edc8d1f2 (layouts/classy) Disabled footer on Classy layout for more 'classy' look 2021-04-25 23:15:38 +03:00
sercan 9dde624bb5 (icons) Added Material Solid icons 2021-04-25 23:14:13 +03:00
sercan a5a27d0a51 (icons) Added Material Solid icons 2021-04-25 13:23:26 +03:00
sercan 85ea34a6ce (Contacts) Fixed: Clicking on the tag checkbox on tag panel breaks the toggling 2021-04-25 12:42:41 +03:00
sercan 9b059f8d0d (icons/heroicons) Updated heroicons to v1.0.1 2021-04-22 22:05:59 +03:00
sercan df48ad1c56 (apps/file-manager) Removed unnecessary imports 2021-04-22 11:24:16 +03:00
sercan 6a113a5317 (icons/heroicons-outline) Fixed: academic-cap icon path fills breaks the icon 2021-04-22 11:23:21 +03:00
sercan 4b268e5d1b (apps/file-manager) Better grid 2021-04-20 22:44:12 +03:00
sercan 4bf11591a2 (Assets) Added avatar images back 2021-04-19 13:08:24 +03:00
sercan f45a605b4e Preparing the starter 2021-04-15 17:43:28 +03:00
sercan c150a8902c Starter 2021-04-15 17:23:49 +03:00
sercan 5e8701f517 Fixed the landing 2021-04-15 17:23:17 +03:00
sercan 700d52d815 Fuse v12.0.0 2021-04-15 17:13:46 +03:00
763 changed files with 115600 additions and 37505 deletions
+17
View File
@@ -0,0 +1,17 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
+15 -5
View File
@@ -1,6 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset=utf-8
end_of_line=lf
insert_final_newline=false
indent_style=space
indent_size=4
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
+95
View File
@@ -0,0 +1,95 @@
{
"root": true,
"env": {
"es6": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/ng-cli-compat",
"plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "",
"style": "camelCase"
}
],
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/explicit-member-accessibility": [
"off",
{
"accessibility": "explicit"
}
],
"@typescript-eslint/no-inferrable-types": "off",
"arrow-parens": [
"error",
"as-needed",
{
"requireForBlockBody": true
}
],
"brace-style": [
"off",
"off"
],
"import/order": "off",
"max-len": [
"error",
{
"ignorePattern": "^import |^export | implements",
"code": 180
}
],
"no-underscore-dangle": "off",
"object-shorthand": "off",
"quote-props": [
"error",
"consistent"
],
"quotes": [
"error",
"single"
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}
+6
View File
@@ -4,10 +4,15 @@
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
# IDEs and editors
/.idea
.project
@@ -23,6 +28,7 @@
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
-1
View File
@@ -1 +0,0 @@
save-prefix=''
+1
View File
@@ -0,0 +1 @@
12
+70 -14
View File
@@ -1,16 +1,72 @@
// -----------------------------------------------------------------------------------------------------
// @ Image/Vector/Icon Credits
// @ 3rd party credits
// -----------------------------------------------------------------------------------------------------
Avatars - https://uifaces.co/
Flag icons - http://www.famfamfam.com/lab/icons/flags/
Frame vector created by Freepik - https://www.freepik.com/free-photos-vectors/frame
A Walk Amongst Friends - Photo by Kristin Ellis on Unsplash - https://unsplash.com/photos/CbZOGbazDWQ
Sunrise at Moraine Lake - Photo by Marlon Martinez on Unsplash - https://unsplash.com/photos/woNYcfrnp9M
Braies Lake - Photo by Luca Nicoletti on Unsplash - https://unsplash.com/photos/dH-L5zPcv3E
Lago di Sorapis - Photo by eberhard grossgasteiger on Unsplash - https://unsplash.com/photos/6uDg_zb20EM
Lago di Braies - Photo by Salmen Bejaoui on Unsplash - https://unsplash.com/photos/uXTozY3CcQg
Reaching - Photo by Justin Novello on Unsplash - https://unsplash.com/photos/Y14TNvIDllM
Yosemite - Photo by Tim Mossholder on Unsplash - https://unsplash.com/photos/ZCrtRSSUpGI
Never Stop Changing - Photo by John Westrock on Unsplash - https://unsplash.com/photos/_GY56uSG70U
Fall glow - Photo by Casey Horner on Unsplash - https://unsplash.com/photos/gz19zOdgN7w
First snow - Photo by eberhard grossgasteiger on Unsplash - https://unsplash.com/photos/LRrGf6dBjA4
// Flags
https://github.com/Yummygum/flagpack-core
// Icons
Material - https://material.io/tools/icons
Feather - https://feathericons.com/
Heroicons - https://github.com/refactoringui/heroicons
Iconsmind - https://iconsmind.com/
// Avatars
https://uifaces.co
// 404, 500 & Maintenance
https://undraw.co
// Mail app
Photo by Riccardo Chiarini on Unsplash - https://unsplash.com/photos/2VDa8bnLM8c
Photo by Johannes Plenio on Unsplash - https://unsplash.com/photos/RwHv7LgeC7s
Photo by Jamie Davies on Unsplash - https://unsplash.com/photos/Hao52Fu9-F8
Photo by Christian Joudrey on Unsplash - https://unsplash.com/photos/mWRR1xj95hg
// Profile page
Photo by Alex Knight on Unsplash - https://unsplash.com/photos/DpPutJwgyW8
// Cards
Photo by Kym Ellis on Unsplash - https://unsplash.com/photos/RPT3AjdXlZc
Photo by Patrick Hendry on Unsplash - https://unsplash.com/photos/Qgxk3PQsMiI
Photo by Hailey Kean on Unsplash - https://unsplash.com/photos/QxjsOlFNr_4
Photo by Nathan Anderson on Unsplash - https://unsplash.com/photos/mG8ShlWrMDI
Photo by Adrian Infernus on Unsplash - https://unsplash.com/photos/5apewqWk978
Photo by freestocks.org on Unsplash - https://unsplash.com/photos/c73TZ2sIU38
Photo by Tim Marshall on Unsplash - https://unsplash.com/photos/PKSCrmZdvwA
Photo by Daniel Koponyas on Unsplash - https://unsplash.com/photos/rbiLY6ZwvXQ
Photo by John Westrock on Unsplash - https://unsplash.com/photos/LCesauDseu8
Photo by Gabriel Sollmann on Unsplash - https://unsplash.com/photos/kFWj9y-tJB4
Photo by Kevin Wolf on Unsplash - https://unsplash.com/photos/BJyjgEdNTPs
Photo by Luca Bravo on Unsplash - https://unsplash.com/photos/hFzIoD0F_i8
Photo by Ian Baldwin on Unsplash - https://unsplash.com/photos/Dlj-SxxTlQ0
Photo by Ben Kolde on Unsplash - https://unsplash.com/photos/KRTFIBOfcFw
Photo by Chad Peltola on Unsplash - https://unsplash.com/photos/BTvQ2ET_iKc
Photo by rocknwool on Unsplash - https://unsplash.com/photos/r56oO1V5oms
Photo by Vita Vilcina on Unsplash - https://unsplash.com/photos/KtOid0FLjqU
Photo by Jia Ye on Unsplash - https://unsplash.com/photos/y8ZnQqgohLk
Photo by Parker Whitson on Unsplash - https://unsplash.com/photos/OlTYIqTjmVM
Photo by Dorian Hurst on Unsplash - https://unsplash.com/photos/a9uWPQlIbYc
Photo by Everaldo Coelho on Unsplash - https://unsplash.com/photos/KPaSCpklCZw
Photo by eberhard grossgasteiger on Unsplash - https://unsplash.com/photos/fh2JefbNlII
Photo by Orlova Maria on Unsplash - https://unsplash.com/photos/p8y4dWEMGMU
Photo by Jake Blucker on Unsplash - https://unsplash.com/photos/tMzCrBkM99Y
Photo by Jerry Zhang on Unsplash - https://unsplash.com/photos/oIBcow6n36s
Photo by John Cobb on Unsplash - https://unsplash.com/photos/IE_sifhay7o
Photo by Dan Gold on Unsplash - https://unsplash.com/photos/mDlhOIfGxNI
Photo by Ana Toma on Unsplash - https://unsplash.com/photos/XsGwe6gYg0c
Photo by Andrea on Unsplash - https://unsplash.com/photos/1AWY0N960Sk
Photo by Aswin on Unsplash - https://unsplash.com/photos/_roUcFWstas
Photo by Justin Kauffman on Unsplash - https://unsplash.com/photos/aWG_dqyhI0A
Photo by Barna Bartis on Unsplash - https://unsplash.com/photos/VVoBQqWrvkc
Photo by Kyle Hinkson on Unsplash - https://unsplash.com/photos/3439EnvnAGo
Photo by Spencer Watson on Unsplash - https://unsplash.com/photos/5TBf16GnHKg
Photo by adrian on Unsplash - https://unsplash.com/photos/1wrzvwoK8A4
Photo by Christopher Rusev on Unsplash - https://unsplash.com/photos/7gKWgCRixf0
Photo by Stephen Leonardi on Unsplash - https://unsplash.com/photos/MDmwQVgDHHM
Photo by Dwinanda Nurhanif Mujito on Unsplash - https://unsplash.com/photos/pKT5Mg16w_w
Photo by Humphrey Muleba on Unsplash - https://unsplash.com/photos/Zuvf5mxT5fs
Photo by adrian on Unsplash - https://unsplash.com/photos/PNRxLFPMyJY
Photo by Dahee Son on Unsplash - https://unsplash.com/photos/tV06QVJXVxU
Photo by Zachary Kyra-Derksen on Unsplash - https://unsplash.com/photos/vkqS7vLQUtg
Photo by Rodrigo Soares on Unsplash - https://unsplash.com/photos/8BFWBUkSqQo
-2
View File
@@ -1,2 +0,0 @@
This project is protected by Envato's Regular License. For more information,
check the official license page at https://themeforest.net/licenses/terms/regular
+6
View File
@@ -0,0 +1,6 @@
Envato Standard License
Copyright (c) Sercan Yemen <sercanyemen@gmail.com>
This project is protected by Envato's Standard License. For more information,
check the official license page at [https://themeforest.net/licenses/standard](https://themeforest.net/licenses/standard)
+5 -12
View File
@@ -1,12 +1,6 @@
# Fuse - Angular
# Fuse - Admin template and Starter project for Angular
Material Design Admin Template with Angular 7+ and Angular Material
## The Community
Share your ideas, discuss Fuse and help each other.
[Click here](http://fusetheme.com/community) to see our Community page.
This project was generated with [Angular CLI](https://github.com/angular/angular-cli)
## Development server
@@ -18,7 +12,7 @@ Run `ng generate component component-name` to generate a new component. You can
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
@@ -26,9 +20,8 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
+76 -91
View File
@@ -4,90 +4,101 @@
"newProjectRoot": "projects",
"projects": {
"fuse": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {
"@schematics/angular:component": {
"styleext": "scss"
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"outputPath": "dist/fuse",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"allowedCommonJsDependencies": [
"apexcharts",
"highlight.js",
"crypto-js/enc-utf8",
"crypto-js/hmac-sha256",
"crypto-js/enc-base64"
],
"assets": [
"src/favicon.ico",
"src/assets"
"src/favicon-16x16.png",
"src/favicon-32x32.png",
"src/assets",
{
"glob": "_redirects",
"input": "src",
"output": "/"
}
],
"stylePreprocessorOptions": {
"includePaths": [
"src/@fuse/styles"
]
},
"styles": [
"src/styles.scss"
"src/@fuse/styles/tailwind.scss",
"src/@fuse/styles/themes.scss",
"src/styles/vendors.scss",
"src/@fuse/styles/main.scss",
"src/styles/styles.scss",
"src/styles/tailwind.scss"
],
"scripts": [],
"showCircularDependencies": false
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "3mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "75kb",
"maximumError": "90kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
"outputHashing": "all"
},
"ec": {
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"extractCss": true
},
"hmr": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.hmr.ts"
}
]
"namedChunks": true
}
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "fuse:build"
},
"configurations": {
"production": {
"browserTarget": "fuse:build:production"
},
"hmr": {
"hmr": true,
"browserTarget": "fuse:build:hmr"
},
"ec": {
"browserTarget": "fuse:build:ec"
"development": {
"browserTarget": "fuse:build:development"
}
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
@@ -100,60 +111,34 @@
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"src/styles.scss"
],
"scripts": [],
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/favicon-16x16.png",
"src/favicon-32x32.png",
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"fuse-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "fuse:serve"
},
"configurations": {
"production": {
"devServerTarget": "fuse:serve:production"
}
"styles": [
"src/styles/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"builder": "@angular-eslint/builder:lint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}
},
"defaultProject": "fuse"
"defaultProject": "fuse",
"cli": {
"defaultCollection": "@angular-eslint/schematics"
}
}
-31
View File
@@ -1,31 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter} = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs : [
'./src/**/*.e2e-spec.ts'
],
capabilities : {
'browserName': 'chrome'
},
directConnect : true,
baseUrl : 'http://localhost:4200/',
framework : 'jasmine',
jasmineNodeOpts : {
showColors : true,
defaultTimeoutInterval: 30000,
print : function ()
{
}
},
onPrepare()
{
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json')
});
jasmine.getEnv().addReporter(new SpecReporter({spec: {displayStacktrace: true}}));
}
};
-14
View File
@@ -1,14 +0,0 @@
import { FusePage } from './app.po';
describe('Fuse App', () => {
let page: FusePage;
beforeEach(() => {
page = new FusePage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to Fuse!');
});
});
-11
View File
@@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class FusePage {
navigateTo(): any {
return browser.get('/');
}
getParagraphText(): any {
return element(by.css('app #main')).getText();
}
}
-13
View File
@@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
+45
View File
@@ -0,0 +1,45 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config)
{
config.set({
basePath : '',
frameworks : ['jasmine', '@angular-devkit/build-angular'],
plugins : [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client : {
jasmine : {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter : {
dir : require('path').join(__dirname, './coverage/fuse'),
subdir : '.',
reporters: [
{type: 'html'},
{type: 'text-summary'}
]
},
reporters : ['progress', 'kjhtml'],
port : 9876,
colors : true,
logLevel : config.LOG_INFO,
autoWatch : true,
browsers : ['Chrome'],
singleRun : false,
restartOnFileChange: true
});
};
+16814 -11793
View File
File diff suppressed because it is too large Load Diff
+89 -89
View File
@@ -1,91 +1,91 @@
{
"name": "fuse",
"version": "7.1.0",
"license": "https://themeforest.net/licenses/terms/regular",
"scripts": {
"ng": "ng",
"start": "ng serve --open",
"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",
"build-prod-stats": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --prod --stats-json",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"bundle-report": "webpack-bundle-analyzer dist/stats.json"
},
"private": true,
"dependencies": {
"@agm/core": "1.0.0-beta.5",
"@angular/animations": "7.2.1",
"@angular/cdk": "7.2.1",
"@angular/common": "7.2.1",
"@angular/compiler": "7.2.1",
"@angular/core": "7.2.1",
"@angular/flex-layout": "7.0.0-beta.23",
"@angular/forms": "7.2.1",
"@angular/http": "7.2.1",
"@angular/material": "7.2.1",
"@angular/material-moment-adapter": "7.2.1",
"@angular/platform-browser": "7.2.1",
"@angular/platform-browser-dynamic": "7.2.1",
"@angular/router": "7.2.1",
"@ngrx/effects": "7.0.0",
"@ngrx/router-store": "7.0.0",
"@ngrx/store": "7.0.0",
"@ngrx/store-devtools": "7.0.0",
"@ngx-translate/core": "11.0.1",
"@swimlane/dragula": "3.7.3",
"@swimlane/ngx-charts": "10.0.0",
"@swimlane/ngx-datatable": "14.0.0",
"@swimlane/ngx-dnd": "6.0.0",
"@types/prismjs": "1.9.0",
"angular-calendar": "0.26.4",
"angular-in-memory-web-api": "0.8.0",
"chart.js": "2.7.3",
"classlist.js": "1.1.20150312",
"core-js": "2.6.2",
"d3": "5.7.0",
"date-fns": "1.30.1",
"hammerjs": "2.0.8",
"lodash": "4.17.11",
"moment": "2.23.0",
"ng2-charts": "1.6.0",
"ngrx-store-freeze": "0.2.4",
"ngx-color-picker": "7.3.0",
"ngx-cookie-service": "2.1.0",
"perfect-scrollbar": "1.4.0",
"prismjs": "1.15.0",
"rxjs": "6.3.3",
"rxjs-compat": "6.3.3",
"web-animations-js": "2.3.1",
"zone.js": "0.8.28"
},
"devDependencies": {
"@angular/cli": "7.2.2",
"@angular/compiler-cli": "7.2.1",
"@angular/language-service": "7.2.1",
"@angular-devkit/build-angular": "0.12.2",
"@angularclass/hmr": "2.1.3",
"@types/jasmine": "2.8.14",
"@types/jasminewd2": "2.0.6",
"@types/lodash": "4.14.119",
"@types/node": "8.9.5",
"codelyzer": "4.5.0",
"jasmine-core": "2.99.1",
"jasmine-spec-reporter": "4.2.1",
"karma": "3.1.4",
"karma-chrome-launcher": "2.2.0",
"karma-coverage-istanbul-reporter": "2.0.4",
"karma-jasmine": "1.1.2",
"karma-jasmine-html-reporter": "0.2.2",
"protractor": "5.4.2",
"ts-node": "7.0.1",
"tslib": "1.9.3",
"tslint": "5.11.0",
"typescript": "3.2.2",
"webpack-bundle-analyzer": "3.0.3"
}
"name": "@fuse/starter",
"version": "13.6.1",
"description": "Fuse - Angular Admin Template and Starter Project",
"author": "https://themeforest.net/user/srcn",
"license": "https://themeforest.net/licenses/standard",
"private": true,
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint"
},
"dependencies": {
"@angular/animations": "12.2.3",
"@angular/cdk": "12.2.3",
"@angular/common": "12.2.3",
"@angular/compiler": "12.2.3",
"@angular/core": "12.2.3",
"@angular/forms": "12.2.3",
"@angular/material": "12.2.3",
"@angular/material-moment-adapter": "12.2.3",
"@angular/platform-browser": "12.2.3",
"@angular/platform-browser-dynamic": "12.2.3",
"@angular/router": "12.2.3",
"@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2",
"@fullcalendar/daygrid": "4.4.2",
"@fullcalendar/interaction": "4.4.2",
"@fullcalendar/list": "4.4.2",
"@fullcalendar/moment": "4.4.2",
"@fullcalendar/rrule": "4.4.2",
"@fullcalendar/timegrid": "4.4.2",
"@ngneat/transloco": "2.22.0",
"apexcharts": "3.28.1",
"crypto-js": "3.3.0",
"highlight.js": "11.2.0",
"lodash-es": "4.17.21",
"moment": "2.29.1",
"ng-apexcharts": "1.5.12",
"ngx-markdown": "12.0.1",
"ngx-quill": "14.3.0",
"perfect-scrollbar": "1.5.2",
"quill": "1.3.7",
"rrule": "2.6.8",
"rxjs": "6.6.7",
"tslib": "2.3.1",
"web-animations-js": "2.3.2",
"zone.js": "0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "12.2.3",
"@angular-eslint/builder": "12.3.1",
"@angular-eslint/eslint-plugin": "12.3.1",
"@angular-eslint/eslint-plugin-template": "12.3.1",
"@angular-eslint/schematics": "12.3.1",
"@angular-eslint/template-parser": "12.3.1",
"@angular/cli": "12.2.3",
"@angular/compiler-cli": "12.2.3",
"@tailwindcss/aspect-ratio": "0.2.1",
"@tailwindcss/line-clamp": "0.2.1",
"@tailwindcss/typography": "0.4.1",
"@types/chroma-js": "2.1.3",
"@types/crypto-js": "3.1.47",
"@types/highlight.js": "10.1.0",
"@types/jasmine": "3.8.2",
"@types/lodash": "4.14.172",
"@types/lodash-es": "4.17.4",
"@types/node": "12.20.21",
"@typescript-eslint/eslint-plugin": "4.30.0",
"@typescript-eslint/parser": "4.30.0",
"autoprefixer": "10.3.3",
"chroma-js": "2.1.2",
"eslint": "7.32.0",
"eslint-plugin-import": "2.24.2",
"eslint-plugin-jsdoc": "36.0.8",
"eslint-plugin-prefer-arrow": "1.2.3",
"jasmine-core": "3.8.0",
"karma": "6.3.4",
"karma-chrome-launcher": "3.1.0",
"karma-coverage": "2.0.3",
"karma-jasmine": "4.0.1",
"karma-jasmine-html-reporter": "1.7.0",
"lodash": "4.17.21",
"postcss": "8.3.6",
"tailwindcss": "2.2.9",
"typescript": "4.3.5"
}
}
+14
View File
@@ -0,0 +1,14 @@
export class FuseAnimationCurves
{
static standard = 'cubic-bezier(0.4, 0.0, 0.2, 1)';
static deceleration = 'cubic-bezier(0.0, 0.0, 0.2, 1)';
static acceleration = 'cubic-bezier(0.4, 0.0, 1, 1)';
static sharp = 'cubic-bezier(0.4, 0.0, 0.6, 1)';
}
export class FuseAnimationDurations
{
static complex = '375ms';
static entering = '225ms';
static exiting = '195ms';
}
+34
View File
@@ -0,0 +1,34 @@
import { animate, state, style, transition, trigger } from '@angular/animations';
import { FuseAnimationCurves, FuseAnimationDurations } from '@fuse/animations/defaults';
// -----------------------------------------------------------------------------------------------------
// @ Expand / collapse
// -----------------------------------------------------------------------------------------------------
const expandCollapse = trigger('expandCollapse',
[
state('void, collapsed',
style({
height: '0'
})
),
state('*, expanded',
style('*')
),
// Prevent the transition if the state is false
transition('void <=> false, collapsed <=> false, expanded <=> false', []),
// Transition
transition('void <=> *, collapsed <=> expanded',
animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
export { expandCollapse };
+330
View File
@@ -0,0 +1,330 @@
import { animate, state, style, transition, trigger } from '@angular/animations';
import { FuseAnimationCurves, FuseAnimationDurations } from '@fuse/animations/defaults';
// -----------------------------------------------------------------------------------------------------
// @ Fade in
// -----------------------------------------------------------------------------------------------------
const fadeIn = trigger('fadeIn',
[
state('void',
style({
opacity: 0
})
),
state('*',
style({
opacity: 1
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade in top
// -----------------------------------------------------------------------------------------------------
const fadeInTop = trigger('fadeInTop',
[
state('void',
style({
opacity : 0,
transform: 'translate3d(0, -100%, 0)'
})
),
state('*',
style({
opacity : 1,
transform: 'translate3d(0, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade in bottom
// -----------------------------------------------------------------------------------------------------
const fadeInBottom = trigger('fadeInBottom',
[
state('void',
style({
opacity : 0,
transform: 'translate3d(0, 100%, 0)'
})
),
state('*',
style({
opacity : 1,
transform: 'translate3d(0, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade in left
// -----------------------------------------------------------------------------------------------------
const fadeInLeft = trigger('fadeInLeft',
[
state('void',
style({
opacity : 0,
transform: 'translate3d(-100%, 0, 0)'
})
),
state('*',
style({
opacity : 1,
transform: 'translate3d(0, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade in right
// -----------------------------------------------------------------------------------------------------
const fadeInRight = trigger('fadeInRight',
[
state('void',
style({
opacity : 0,
transform: 'translate3d(100%, 0, 0)'
})
),
state('*',
style({
opacity : 1,
transform: 'translate3d(0, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade out
// -----------------------------------------------------------------------------------------------------
const fadeOut = trigger('fadeOut',
[
state('*',
style({
opacity: 1
})
),
state('void',
style({
opacity: 0
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade out top
// -----------------------------------------------------------------------------------------------------
const fadeOutTop = trigger('fadeOutTop',
[
state('*',
style({
opacity : 1,
transform: 'translate3d(0, 0, 0)'
})
),
state('void',
style({
opacity : 0,
transform: 'translate3d(0, -100%, 0)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade out bottom
// -----------------------------------------------------------------------------------------------------
const fadeOutBottom = trigger('fadeOutBottom',
[
state('*',
style({
opacity : 1,
transform: 'translate3d(0, 0, 0)'
})
),
state('void',
style({
opacity : 0,
transform: 'translate3d(0, 100%, 0)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade out left
// -----------------------------------------------------------------------------------------------------
const fadeOutLeft = trigger('fadeOutLeft',
[
state('*',
style({
opacity : 1,
transform: 'translate3d(0, 0, 0)'
})
),
state('void',
style({
opacity : 0,
transform: 'translate3d(-100%, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Fade out right
// -----------------------------------------------------------------------------------------------------
const fadeOutRight = trigger('fadeOutRight',
[
state('*',
style({
opacity : 1,
transform: 'translate3d(0, 0, 0)'
})
),
state('void',
style({
opacity : 0,
transform: 'translate3d(100%, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
export { fadeIn, fadeInTop, fadeInBottom, fadeInLeft, fadeInRight, fadeOut, fadeOutTop, fadeOutBottom, fadeOutLeft, fadeOutRight };
+1 -417
View File
@@ -1,417 +1 @@
import { sequence, trigger, animate, style, group, query, transition, animateChild, state, animation, useAnimation, stagger } from '@angular/animations';
const customAnimation = animation([
style({
opacity : '{{opacity}}',
transform: 'scale({{scale}}) translate3d({{x}}, {{y}}, {{z}})'
}),
animate('{{duration}} {{delay}} cubic-bezier(0.0, 0.0, 0.2, 1)', style('*'))
], {
params: {
duration: '200ms',
delay : '0ms',
opacity : '0',
scale : '1',
x : '0',
y : '0',
z : '0'
}
});
export const fuseAnimations = [
trigger('animate', [transition('void => *', [useAnimation(customAnimation)])]),
trigger('animateStagger', [
state('50', style('*')),
state('100', style('*')),
state('200', style('*')),
transition('void => 50',
query('@*',
[
stagger('50ms', [
animateChild()
])
], {optional: true})),
transition('void => 100',
query('@*',
[
stagger('100ms', [
animateChild()
])
], {optional: true})),
transition('void => 200',
query('@*',
[
stagger('200ms', [
animateChild()
])
], {optional: true}))
]),
trigger('fadeInOut', [
state('0', style({
display: 'none',
opacity: 0
})),
state('1', style({
display: 'block',
opacity: 1
})),
transition('1 => 0', animate('300ms ease-out')),
transition('0 => 1', animate('300ms ease-in'))
]),
trigger('slideInOut', [
state('0', style({
height : '0px',
display: 'none'
})),
state('1', style({
height : '*',
display: 'block'
})),
transition('1 => 0', animate('300ms ease-out')),
transition('0 => 1', animate('300ms ease-in'))
]),
trigger('slideIn', [
transition('void => left', [
style({
transform: 'translateX(100%)'
}),
animate('300ms ease-in',
style({
transform: 'translateX(0)'
})
)
]
),
transition('left => void', [
style({
transform: 'translateX(0)'
}),
animate('300ms ease-in',
style({
transform: 'translateX(-100%)'
})
)
]
),
transition('void => right', [
style({
transform: 'translateX(-100%)'
}),
animate('300ms ease-in',
style({
transform: 'translateX(0)'
})
)
]
),
transition('right => void', [
style({
transform: 'translateX(0)'
}),
animate('300ms ease-in',
style({
transform: 'translateX(100%)'
})
)
]
),
]),
trigger('slideInLeft', [
state('void', style({
transform: 'translateX(-100%)',
display : 'none'
})),
state('*', style({
transform: 'translateX(0)',
display : 'flex'
})),
transition('void => *', animate('300ms')),
transition('* => void', animate('300ms'))
]),
trigger('slideInRight', [
state('void', style({
transform: 'translateX(100%)',
display : 'none'
})),
state('*', style({
transform: 'translateX(0)',
display : 'flex'
})),
transition('void => *', animate('300ms')),
transition('* => void', animate('300ms'))
]),
trigger('slideInTop', [
state('void', style({
transform: 'translateY(-100%)',
display : 'none'
})),
state('*', style({
transform: 'translateY(0)',
display : 'flex'
})),
transition('void => *', animate('300ms')),
transition('* => void', animate('300ms'))
]),
trigger('slideInBottom', [
state('void',
style({
transform: 'translateY(100%)',
display : 'none'
})),
state('*', style({
transform: 'translateY(0)',
display : 'flex'
})),
transition('void => *', animate('300ms')),
transition('* => void', animate('300ms'))
]),
trigger('expandCollapse', [
state('void', style({
height: '0px'
})),
state('*', style({
height: '*'
})),
transition('void => *', animate('300ms ease-out')),
transition('* => void', animate('300ms ease-in'))
]),
// -----------------------------------------------------------------------------------------------------
// @ Router animations
// -----------------------------------------------------------------------------------------------------
trigger('routerTransitionLeft', [
transition('* => *', [
query('content > :enter, content > :leave', [
style({
position: 'absolute',
top : 0,
bottom : 0,
left : 0,
right : 0
})
], {optional: true}),
query('content > :enter', [
style({
transform: 'translateX(100%)',
opacity : 0
})
], {optional: true}),
sequence([
group([
query('content > :leave', [
style({
transform: 'translateX(0)',
opacity : 1
}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
transform: 'translateX(-100%)',
opacity : 0
}))
], {optional: true}),
query('content > :enter', [
style({transform: 'translateX(100%)'}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
transform: 'translateX(0%)',
opacity : 1
}))
], {optional: true})
]),
query('content > :leave', animateChild(), {optional: true}),
query('content > :enter', animateChild(), {optional: true})
])
])
]),
trigger('routerTransitionRight', [
transition('* => *', [
query('content > :enter, content > :leave', [
style({
position: 'absolute',
top : 0,
bottom : 0,
left : 0,
right : 0
})
], {optional: true}),
query('content > :enter', [
style({
transform: 'translateX(-100%)',
opacity : 0
})
], {optional: true}),
sequence([
group([
query('content > :leave', [
style({
transform: 'translateX(0)',
opacity : 1
}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
transform: 'translateX(100%)',
opacity : 0
}))
], {optional: true}),
query('content > :enter', [
style({transform: 'translateX(-100%)'}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
transform: 'translateX(0%)',
opacity : 1
}))
], {optional: true})
]),
query('content > :leave', animateChild(), {optional: true}),
query('content > :enter', animateChild(), {optional: true})
])
])
]),
trigger('routerTransitionUp', [
transition('* => *', [
query('content > :enter, content > :leave', [
style({
position: 'absolute',
top : 0,
bottom : 0,
left : 0,
right : 0
})
], {optional: true}),
query('content > :enter', [
style({
transform: 'translateY(100%)',
opacity : 0
})
], {optional: true}),
group([
query('content > :leave', [
style({
transform: 'translateY(0)',
opacity : 1
}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
transform: 'translateY(-100%)',
opacity : 0
}))
], {optional: true}),
query('content > :enter', [
style({transform: 'translateY(100%)'}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
transform: 'translateY(0%)',
opacity : 1
}))
], {optional: true})
]),
query('content > :leave', animateChild(), {optional: true}),
query('content > :enter', animateChild(), {optional: true})
])
]),
trigger('routerTransitionDown', [
transition('* => *', [
query('content > :enter, content > :leave', [
style({
position: 'absolute',
top : 0,
bottom : 0,
left : 0,
right : 0
})
], {optional: true}),
query('content > :enter', [
style({
transform: 'translateY(-100%)',
opacity : 0
})
], {optional: true}),
sequence([
group([
query('content > :leave', [
style({
transform: 'translateY(0)',
opacity : 1
}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
transform: 'translateY(100%)',
opacity : 0
}))
], {optional: true}),
query('content > :enter', [
style({transform: 'translateY(-100%)'}),
animate('600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
transform: 'translateY(0%)',
opacity : 1
}))
], {optional: true})
]),
query('content > :leave', animateChild(), {optional: true}),
query('content > :enter', animateChild(), {optional: true})
])
])
]),
trigger('routerTransitionFade', [
transition('* => *', group([
query('content > :enter, content > :leave ', [
style({
position: 'absolute',
top : 0,
bottom : 0,
left : 0,
right : 0
})
], {optional: true}),
query('content > :enter', [
style({
opacity: 0
})
], {optional: true}),
query('content > :leave', [
style({
opacity: 1
}),
animate('300ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
opacity: 0
}))
], {optional: true}),
query('content > :enter', [
style({
opacity: 0
}),
animate('300ms cubic-bezier(0.0, 0.0, 0.2, 1)',
style({
opacity: 1
}))
], {optional: true}),
query('content > :enter', animateChild(), {optional: true}),
query('content > :leave', animateChild(), {optional: true})
]))
])
];
export * from '@fuse/animations/public-api';
+15
View File
@@ -0,0 +1,15 @@
import { expandCollapse } from '@fuse/animations/expand-collapse';
import { fadeIn, fadeInBottom, fadeInLeft, fadeInRight, fadeInTop, fadeOut, fadeOutBottom, fadeOutLeft, fadeOutRight, fadeOutTop } from '@fuse/animations/fade';
import { shake } from '@fuse/animations/shake';
import { slideInBottom, slideInLeft, slideInRight, slideInTop, slideOutBottom, slideOutLeft, slideOutRight, slideOutTop } from '@fuse/animations/slide';
import { zoomIn, zoomOut } from '@fuse/animations/zoom';
export const fuseAnimations = [
expandCollapse,
fadeIn, fadeInTop, fadeInBottom, fadeInLeft, fadeInRight,
fadeOut, fadeOutTop, fadeOutBottom, fadeOutLeft, fadeOutRight,
shake,
slideInTop, slideInBottom, slideInLeft, slideInRight,
slideOutTop, slideOutBottom, slideOutLeft, slideOutRight,
zoomIn, zoomOut
];
+73
View File
@@ -0,0 +1,73 @@
import { animate, keyframes, style, transition, trigger } from '@angular/animations';
// -----------------------------------------------------------------------------------------------------
// @ Shake
// -----------------------------------------------------------------------------------------------------
const shake = trigger('shake',
[
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *, * => true',
[
animate('{{timings}}',
keyframes([
style({
transform: 'translate3d(0, 0, 0)',
offset : 0
}),
style({
transform: 'translate3d(-10px, 0, 0)',
offset : 0.1
}),
style({
transform: 'translate3d(10px, 0, 0)',
offset : 0.2
}),
style({
transform: 'translate3d(-10px, 0, 0)',
offset : 0.3
}),
style({
transform: 'translate3d(10px, 0, 0)',
offset : 0.4
}),
style({
transform: 'translate3d(-10px, 0, 0)',
offset : 0.5
}),
style({
transform: 'translate3d(10px, 0, 0)',
offset : 0.6
}),
style({
transform: 'translate3d(-10px, 0, 0)',
offset : 0.7
}),
style({
transform: 'translate3d(10px, 0, 0)',
offset : 0.8
}),
style({
transform: 'translate3d(-10px, 0, 0)',
offset : 0.9
}),
style({
transform: 'translate3d(0, 0, 0)',
offset : 1
})
])
)
],
{
params: {
timings: '0.8s cubic-bezier(0.455, 0.03, 0.515, 0.955)'
}
}
)
]
);
export { shake };
+252
View File
@@ -0,0 +1,252 @@
import { animate, state, style, transition, trigger } from '@angular/animations';
import { FuseAnimationCurves, FuseAnimationDurations } from '@fuse/animations/defaults';
// -----------------------------------------------------------------------------------------------------
// @ Slide in top
// -----------------------------------------------------------------------------------------------------
const slideInTop = trigger('slideInTop',
[
state('void',
style({
transform: 'translate3d(0, -100%, 0)'
})
),
state('*',
style({
transform: 'translate3d(0, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Slide in bottom
// -----------------------------------------------------------------------------------------------------
const slideInBottom = trigger('slideInBottom',
[
state('void',
style({
transform: 'translate3d(0, 100%, 0)'
})
),
state('*',
style({
transform: 'translate3d(0, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Slide in left
// -----------------------------------------------------------------------------------------------------
const slideInLeft = trigger('slideInLeft',
[
state('void',
style({
transform: 'translate3d(-100%, 0, 0)'
})
),
state('*',
style({
transform: 'translate3d(0, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Slide in right
// -----------------------------------------------------------------------------------------------------
const slideInRight = trigger('slideInRight',
[
state('void',
style({
transform: 'translate3d(100%, 0, 0)'
})
),
state('*',
style({
transform: 'translate3d(0, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Slide out top
// -----------------------------------------------------------------------------------------------------
const slideOutTop = trigger('slideOutTop',
[
state('*',
style({
transform: 'translate3d(0, 0, 0)'
})
),
state('void',
style({
transform: 'translate3d(0, -100%, 0)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Slide out bottom
// -----------------------------------------------------------------------------------------------------
const slideOutBottom = trigger('slideOutBottom',
[
state('*',
style({
transform: 'translate3d(0, 0, 0)'
})
),
state('void',
style({
transform: 'translate3d(0, 100%, 0)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Slide out left
// -----------------------------------------------------------------------------------------------------
const slideOutLeft = trigger('slideOutLeft',
[
state('*',
style({
transform: 'translate3d(0, 0, 0)'
})
),
state('void',
style({
transform: 'translate3d(-100%, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Slide out right
// -----------------------------------------------------------------------------------------------------
const slideOutRight = trigger('slideOutRight',
[
state('*',
style({
transform: 'translate3d(0, 0, 0)'
})
),
state('void',
style({
transform: 'translate3d(100%, 0, 0)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
export { slideInTop, slideInBottom, slideInLeft, slideInRight, slideOutTop, slideOutBottom, slideOutLeft, slideOutRight };
+73
View File
@@ -0,0 +1,73 @@
import { animate, state, style, transition, trigger } from '@angular/animations';
import { FuseAnimationCurves, FuseAnimationDurations } from '@fuse/animations/defaults';
// -----------------------------------------------------------------------------------------------------
// @ Zoom in
// -----------------------------------------------------------------------------------------------------
const zoomIn = trigger('zoomIn',
[
state('void',
style({
opacity : 0,
transform: 'scale(0.5)'
})
),
state('*',
style({
opacity : 1,
transform: 'scale(1)'
})
),
// Prevent the transition if the state is false
transition('void => false', []),
// Transition
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
]
);
// -----------------------------------------------------------------------------------------------------
// @ Zoom out
// -----------------------------------------------------------------------------------------------------
const zoomOut = trigger('zoomOut',
[
state('*',
style({
opacity : 1,
transform: 'scale(1)'
})
),
state('void',
style({
opacity : 0,
transform: 'scale(0.5)'
})
),
// Prevent the transition if the state is false
transition('false => void', []),
// Transition
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
]
);
export { zoomIn, zoomOut };
@@ -0,0 +1,82 @@
<div
class="fuse-alert-container"
*ngIf="!dismissible || dismissible && !dismissed"
[@fadeIn]="!dismissed"
[@fadeOut]="!dismissed">
<!-- Border -->
<div
class="fuse-alert-border"
*ngIf="appearance === 'border'"></div>
<!-- Icon -->
<div
class="fuse-alert-icon"
*ngIf="showIcon">
<!-- Custom icon -->
<div class="fuse-alert-custom-icon">
<ng-content select="[fuseAlertIcon]"></ng-content>
</div>
<!-- Default icons -->
<div class="fuse-alert-default-icon">
<mat-icon
*ngIf="type === 'primary'"
[svgIcon]="'heroicons_solid:check-circle'"></mat-icon>
<mat-icon
*ngIf="type === 'accent'"
[svgIcon]="'heroicons_solid:check-circle'"></mat-icon>
<mat-icon
*ngIf="type === 'warn'"
[svgIcon]="'heroicons_solid:x-circle'"></mat-icon>
<mat-icon
*ngIf="type === 'basic'"
[svgIcon]="'heroicons_solid:check-circle'"></mat-icon>
<mat-icon
*ngIf="type === 'info'"
[svgIcon]="'heroicons_solid:information-circle'"></mat-icon>
<mat-icon
*ngIf="type === 'success'"
[svgIcon]="'heroicons_solid:check-circle'"></mat-icon>
<mat-icon
*ngIf="type === 'warning'"
[svgIcon]="'heroicons_solid:exclamation'"></mat-icon>
<mat-icon
*ngIf="type === 'error'"
[svgIcon]="'heroicons_solid:x-circle'"></mat-icon>
</div>
</div>
<!-- Content -->
<div class="fuse-alert-content">
<div class="fuse-alert-title">
<ng-content select="[fuseAlertTitle]"></ng-content>
</div>
<div class="fuse-alert-message">
<ng-content></ng-content>
</div>
</div>
<!-- Dismiss button -->
<button
class="fuse-alert-dismiss-button"
mat-icon-button
(click)="dismiss()">
<mat-icon [svgIcon]="'heroicons_solid:x'"></mat-icon>
</button>
</div>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,213 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostBinding, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { fuseAnimations } from '@fuse/animations';
import { FuseAlertAppearance, FuseAlertType } from '@fuse/components/alert/alert.types';
import { FuseAlertService } from '@fuse/components/alert/alert.service';
import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@Component({
selector : 'fuse-alert',
templateUrl : './alert.component.html',
styleUrls : ['./alert.component.scss'],
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
animations : fuseAnimations,
exportAs : 'fuseAlert'
})
export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_dismissible: BooleanInput;
static ngAcceptInputType_dismissed: BooleanInput;
static ngAcceptInputType_showIcon: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() appearance: FuseAlertAppearance = 'soft';
@Input() dismissed: boolean = false;
@Input() dismissible: boolean = false;
@Input() name: string = this._fuseUtilsService.randomId();
@Input() showIcon: boolean = true;
@Input() type: FuseAlertType = 'primary';
@Output() readonly dismissedChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _fuseAlertService: FuseAlertService,
private _fuseUtilsService: FuseUtilsService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Host binding for component classes
*/
@HostBinding('class') get classList(): any
{
return {
'fuse-alert-appearance-border' : this.appearance === 'border',
'fuse-alert-appearance-fill' : this.appearance === 'fill',
'fuse-alert-appearance-outline': this.appearance === 'outline',
'fuse-alert-appearance-soft' : this.appearance === 'soft',
'fuse-alert-dismissed' : this.dismissed,
'fuse-alert-dismissible' : this.dismissible,
'fuse-alert-show-icon' : this.showIcon,
'fuse-alert-type-primary' : this.type === 'primary',
'fuse-alert-type-accent' : this.type === 'accent',
'fuse-alert-type-warn' : this.type === 'warn',
'fuse-alert-type-basic' : this.type === 'basic',
'fuse-alert-type-info' : this.type === 'info',
'fuse-alert-type-success' : this.type === 'success',
'fuse-alert-type-warning' : this.type === 'warning',
'fuse-alert-type-error' : this.type === 'error'
};
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Dismissed
if ( 'dismissed' in changes )
{
// Coerce the value to a boolean
this.dismissed = coerceBooleanProperty(changes.dismissed.currentValue);
// Dismiss/show the alert
this._toggleDismiss(this.dismissed);
}
// Dismissible
if ( 'dismissible' in changes )
{
// Coerce the value to a boolean
this.dismissible = coerceBooleanProperty(changes.dismissible.currentValue);
}
// Show icon
if ( 'showIcon' in changes )
{
// Coerce the value to a boolean
this.showIcon = coerceBooleanProperty(changes.showIcon.currentValue);
}
}
/**
* On init
*/
ngOnInit(): void
{
// Subscribe to the dismiss calls
this._fuseAlertService.onDismiss
.pipe(
filter(name => this.name === name),
takeUntil(this._unsubscribeAll)
)
.subscribe(() => {
// Dismiss the alert
this.dismiss();
});
// Subscribe to the show calls
this._fuseAlertService.onShow
.pipe(
filter(name => this.name === name),
takeUntil(this._unsubscribeAll)
)
.subscribe(() => {
// Show the alert
this.show();
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Dismiss the alert
*/
dismiss(): void
{
// Return if the alert is already dismissed
if ( this.dismissed )
{
return;
}
// Dismiss the alert
this._toggleDismiss(true);
}
/**
* Show the dismissed alert
*/
show(): void
{
// Return if the alert is already showing
if ( !this.dismissed )
{
return;
}
// Show the alert
this._toggleDismiss(false);
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Dismiss/show the alert
*
* @param dismissed
* @private
*/
private _toggleDismiss(dismissed: boolean): void
{
// Return if the alert is not dismissible
if ( !this.dismissible )
{
return;
}
// Set the dismissed
this.dismissed = dismissed;
// Execute the observable
this.dismissedChanged.next(this.dismissed);
// Notify the change detector
this._changeDetectorRef.markForCheck();
}
}
@@ -0,0 +1,22 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { FuseAlertComponent } from '@fuse/components/alert/alert.component';
@NgModule({
declarations: [
FuseAlertComponent
],
imports : [
CommonModule,
MatButtonModule,
MatIconModule
],
exports : [
FuseAlertComponent
]
})
export class FuseAlertModule
{
}
@@ -0,0 +1,77 @@
import { Injectable } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class FuseAlertService
{
private readonly _onDismiss: ReplaySubject<string> = new ReplaySubject<string>(1);
private readonly _onShow: ReplaySubject<string> = new ReplaySubject<string>(1);
/**
* Constructor
*/
constructor()
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for onDismiss
*/
get onDismiss(): Observable<any>
{
return this._onDismiss.asObservable();
}
/**
* Getter for onShow
*/
get onShow(): Observable<any>
{
return this._onShow.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Dismiss the alert
*
* @param name
*/
dismiss(name: string): void
{
// Return if the name is not provided
if ( !name )
{
return;
}
// Execute the observable
this._onDismiss.next(name);
}
/**
* Show the dismissed alert
*
* @param name
*/
show(name: string): void
{
// Return if the name is not provided
if ( !name )
{
return;
}
// Execute the observable
this._onShow.next(name);
}
}
+15
View File
@@ -0,0 +1,15 @@
export type FuseAlertAppearance =
| 'border'
| 'fill'
| 'outline'
| 'soft';
export type FuseAlertType =
| 'primary'
| 'accent'
| 'warn'
| 'basic'
| 'info'
| 'success'
| 'warning'
| 'error';
+1
View File
@@ -0,0 +1 @@
export * from '@fuse/components/alert/public-api';
+4
View File
@@ -0,0 +1,4 @@
export * from '@fuse/components/alert/alert.component';
export * from '@fuse/components/alert/alert.module';
export * from '@fuse/components/alert/alert.service';
export * from '@fuse/components/alert/alert.types';
@@ -0,0 +1,30 @@
<!-- Flippable card -->
<ng-container *ngIf="flippable">
<!-- Front -->
<div class="fuse-card-front">
<ng-content select="[fuseCardFront]"></ng-content>
</div>
<!-- Back -->
<div class="fuse-card-back">
<ng-content select="[fuseCardBack]"></ng-content>
</div>
</ng-container>
<!-- Normal card -->
<ng-container *ngIf="!flippable">
<!-- Content -->
<ng-content></ng-content>
<!-- Expansion -->
<div
class="fuse-card-expansion"
*ngIf="expanded"
[@expandCollapse]>
<ng-content select="[fuseCardExpansion]"></ng-content>
</div>
</ng-container>
@@ -0,0 +1,63 @@
fuse-card {
position: relative;
display: flex;
overflow: hidden;
@apply rounded-2xl shadow bg-card;
/* Flippable */
&.fuse-card-flippable {
border-radius: 0;
overflow: visible;
transform-style: preserve-3d;
transition: transform 1s;
perspective: 600px;
background: transparent;
@apply shadow-none;
&.fuse-card-face-back {
.fuse-card-front {
visibility: hidden;
opacity: 0;
transform: rotateY(180deg);
}
.fuse-card-back {
visibility: visible;
opacity: 1;
transform: rotateY(360deg);
}
}
.fuse-card-front,
.fuse-card-back {
display: flex;
flex-direction: column;
flex: 1 1 auto;
z-index: 10;
transition: transform 0.5s ease-out 0s, visibility 0s ease-in 0.2s, opacity 0s ease-in 0.2s;
backface-visibility: hidden;
@apply rounded-2xl shadow bg-card;
}
.fuse-card-front {
position: relative;
opacity: 1;
visibility: visible;
transform: rotateY(0deg);
overflow: hidden;
}
.fuse-card-back {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
opacity: 0;
visibility: hidden;
transform: rotateY(180deg);
overflow: hidden auto;
}
}
}
@@ -0,0 +1,74 @@
import { Component, HostBinding, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { fuseAnimations } from '@fuse/animations';
import { FuseCardFace } from '@fuse/components/card/card.types';
@Component({
selector : 'fuse-card',
templateUrl : './card.component.html',
styleUrls : ['./card.component.scss'],
encapsulation: ViewEncapsulation.None,
animations : fuseAnimations,
exportAs : 'fuseCard'
})
export class FuseCardComponent implements OnChanges
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_expanded: BooleanInput;
static ngAcceptInputType_flippable: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() expanded: boolean = false;
@Input() face: FuseCardFace = 'front';
@Input() flippable: boolean = false;
/**
* Constructor
*/
constructor()
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Host binding for component classes
*/
@HostBinding('class') get classList(): any
{
return {
'fuse-card-expanded' : this.expanded,
'fuse-card-face-back' : this.flippable && this.face === 'back',
'fuse-card-face-front': this.flippable && this.face === 'front',
'fuse-card-flippable' : this.flippable
};
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Expanded
if ( 'expanded' in changes )
{
// Coerce the value to a boolean
this.expanded = coerceBooleanProperty(changes.expanded.currentValue);
}
// Flippable
if ( 'flippable' in changes )
{
// Coerce the value to a boolean
this.flippable = coerceBooleanProperty(changes.flippable.currentValue);
}
}
}
+18
View File
@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FuseCardComponent } from '@fuse/components/card/card.component';
@NgModule({
declarations: [
FuseCardComponent
],
imports : [
CommonModule
],
exports : [
FuseCardComponent
]
})
export class FuseCardModule
{
}
+3
View File
@@ -0,0 +1,3 @@
export type FuseCardFace =
| 'front'
| 'back';
+1
View File
@@ -0,0 +1 @@
export * from '@fuse/components/card/public-api';
+2
View File
@@ -0,0 +1,2 @@
export * from '@fuse/components/card/card.component';
export * from '@fuse/components/card/card.module';
@@ -1,6 +0,0 @@
<h1 matDialogTitle>Confirm</h1>
<div mat-dialog-content>{{confirmMessage}}</div>
<div mat-dialog-actions class="pt-24">
<button mat-raised-button class="mat-accent mr-16" (click)="dialogRef.close(true)">Confirm</button>
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
</div>
@@ -1,24 +0,0 @@
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material';
@Component({
selector : 'fuse-confirm-dialog',
templateUrl: './confirm-dialog.component.html',
styleUrls : ['./confirm-dialog.component.scss']
})
export class FuseConfirmDialogComponent
{
public confirmMessage: string;
/**
* Constructor
*
* @param {MatDialogRef<FuseConfirmDialogComponent>} dialogRef
*/
constructor(
public dialogRef: MatDialogRef<FuseConfirmDialogComponent>
)
{
}
}
@@ -1,20 +0,0 @@
import { NgModule } from '@angular/core';
import { MatButtonModule, MatDialogModule } from '@angular/material';
import { FuseConfirmDialogComponent } from '@fuse/components/confirm-dialog/confirm-dialog.component';
@NgModule({
declarations: [
FuseConfirmDialogComponent
],
imports: [
MatDialogModule,
MatButtonModule
],
entryComponents: [
FuseConfirmDialogComponent
],
})
export class FuseConfirmDialogModule
{
}
@@ -1,39 +0,0 @@
<div class="fuse-countdown">
<div class="time days">
<div class="value">
{{countdown.days}}
</div>
<div class="title">
days
</div>
</div>
<div class="time hours">
<div class="value">
{{countdown.hours}}
</div>
<div class="title">
hours
</div>
</div>
<div class="time minutes">
<div class="value">
{{countdown.minutes}}
</div>
<div class="title">
minutes
</div>
</div>
<div class="time seconds">
<div class="value">
{{countdown.seconds}}
</div>
<div class="title">
seconds
</div>
</div>
</div>
@@ -1,30 +0,0 @@
fuse-countdown {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.fuse-countdown {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
text-align: center;
.time {
display: flex;
flex-direction: column;
padding: 0 12px;
.value {
font-size: 34px;
line-height: 34px;
padding-bottom: 8px;
}
.title {
color: rgba(0, 0, 0, 0.54);
}
}
}
}
@@ -1,110 +0,0 @@
import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { interval, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import * as moment from 'moment';
@Component({
selector : 'fuse-countdown',
templateUrl: './countdown.component.html',
styleUrls : ['./countdown.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseCountdownComponent implements OnInit, OnDestroy
{
// 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();
}
// -----------------------------------------------------------------------------------------------------
// @ 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 in seconds
let diff = eventDate.diff(currDate, 'seconds');
// Calculate the remaining time for the first time so there will be no
// delay on the countdown
this.countdown = this._secondsToRemaining(diff);
// Create a subscribable interval
const countDown = interval(1000)
.pipe(
map(value => {
return diff = diff - 1;
}),
map(value => {
return this._secondsToRemaining(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();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Converts given seconds to a remaining time
*
* @param seconds
* @private
*/
private _secondsToRemaining(seconds): any
{
const timeLeft = moment.duration(seconds, 'seconds');
return {
days : timeLeft.asDays().toFixed(0),
hours : timeLeft.hours(),
minutes: timeLeft.minutes(),
seconds: timeLeft.seconds()
};
}
}
@@ -1,15 +0,0 @@
import { NgModule } from '@angular/core';
import { FuseCountdownComponent } from '@fuse/components/countdown/countdown.component';
@NgModule({
declarations: [
FuseCountdownComponent
],
exports: [
FuseCountdownComponent
],
})
export class FuseCountdownModule
{
}
@@ -1,17 +0,0 @@
@mixin fuse-countdown-theme($theme) {
$foreground: map-get($theme, foreground);
fuse-countdown {
.fuse-countdown {
.time {
.title {
color: map-get($foreground, secondary-text);
}
}
}
}
}
@@ -0,0 +1,101 @@
<div
class="range"
(click)="openPickerPanel()"
#pickerPanelOrigin>
<div class="start">
<div class="date">{{range.startDate}}</div>
<div
class="time"
*ngIf="range.startTime">{{range.startTime}}</div>
</div>
<div class="separator">-</div>
<div class="end">
<div class="date">{{range.endDate}}</div>
<div
class="time"
*ngIf="range.endTime">{{range.endTime}}</div>
</div>
</div>
<ng-template #pickerPanel>
<!-- Start -->
<div class="start">
<div class="month">
<div class="month-header">
<button
class="previous-button"
mat-icon-button
(click)="prev()"
tabindex="1">
<mat-icon [svgIcon]="'heroicons_outline:chevron-left'"></mat-icon>
</button>
<div class="month-label">{{getMonthLabel(1)}}</div>
</div>
<mat-month-view
[(activeDate)]="activeDates.month1"
[dateFilter]="dateFilter()"
[dateClass]="dateClass()"
(click)="$event.stopImmediatePropagation()"
(selectedChange)="onSelectedDateChange($event)"
#matMonthView1>
</mat-month-view>
</div>
<mat-form-field
class="fuse-mat-no-subscript time start-time"
*ngIf="timeRange">
<input
matInput
[autocomplete]="'off'"
[formControl]="startTimeFormControl"
(blur)="updateStartTime($event)"
tabindex="3">
<mat-label>Start time</mat-label>
</mat-form-field>
</div>
<!-- End -->
<div class="end">
<div class="month">
<div class="month-header">
<div class="month-label">{{getMonthLabel(2)}}</div>
<button
class="next-button"
mat-icon-button
(click)="next()"
tabindex="2">
<mat-icon [svgIcon]="'heroicons_outline:chevron-right'"></mat-icon>
</button>
</div>
<mat-month-view
[(activeDate)]="activeDates.month2"
[dateFilter]="dateFilter()"
[dateClass]="dateClass()"
(click)="$event.stopImmediatePropagation()"
(selectedChange)="onSelectedDateChange($event)"
#matMonthView2>
</mat-month-view>
</div>
<mat-form-field
class="fuse-mat-no-subscript time end-time"
*ngIf="timeRange">
<input
matInput
[formControl]="endTimeFormControl"
(blur)="updateEndTime($event)"
tabindex="4">
<mat-label>End time</mat-label>
</mat-form-field>
</div>
</ng-template>
@@ -0,0 +1,292 @@
/* Variables */
$body-cell-padding: 2px;
fuse-date-range {
display: flex;
.range {
display: flex;
align-items: center;
height: 48px;
min-height: 48px;
max-height: 48px;
cursor: pointer;
.start,
.end {
display: flex;
align-items: center;
height: 100%;
padding: 0 16px;
border-radius: 6px;
border-width: 1px;
line-height: 1;
@apply shadow-sm border-gray-300 dark:bg-black dark:bg-opacity-5 dark:border-gray-500;
.date {
white-space: nowrap;
+ .time {
margin-left: 8px;
}
}
.time {
white-space: nowrap;
}
}
.separator {
margin: 0 2px;
@screen sm {
margin: 0 12px;
}
}
}
}
.fuse-date-range-panel {
border-radius: 4px;
padding: 24px;
@apply shadow-2xl bg-card;
.start,
.end {
display: flex;
flex-direction: column;
.month {
max-width: 196px;
min-width: 196px;
width: 196px;
.month-header {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 32px;
margin-bottom: 16px;
.previous-button,
.next-button {
position: absolute;
width: 24px !important;
height: 24px !important;
min-height: 24px !important;
max-height: 24px !important;
line-height: 24px !important;
.mat-icon {
@apply icon-size-5;
}
}
.previous-button {
left: 0;
}
.next-button {
right: 0;
}
.month-label {
font-weight: 500;
@apply text-secondary;
}
}
mat-month-view {
display: flex;
min-height: 188px;
.mat-calendar-table {
width: 100%;
border-collapse: collapse;
tbody {
tr {
&[aria-hidden=true] {
display: none !important;
}
&:first-child {
td:first-child {
&[aria-hidden=true] {
visibility: hidden;
pointer-events: none;
opacity: 0;
}
}
}
td,
td:hover {
&.fuse-date-range {
&:before {
@apply bg-primary-200;
}
.mat-calendar-body-cell-content {
background-color: transparent;
}
}
&.fuse-date-range-start,
&.fuse-date-range-end {
.mat-calendar-body-cell-content {
@apply bg-primary text-on-primary;
}
}
.mat-calendar-body-today {
border: none;
}
}
td.mat-calendar-body-cell {
width: 28px !important;
height: 28px !important;
padding: $body-cell-padding !important;
&.fuse-date-range {
position: relative;
&:before {
content: '';
position: absolute;
top: $body-cell-padding;
right: 0;
bottom: $body-cell-padding;
left: 0;
}
&.fuse-date-range-start {
&:before {
left: $body-cell-padding;
border-radius: 999px 0 0 999px;
}
&.fuse-date-range-end,
&:last-child {
&:before {
right: $body-cell-padding;
border-radius: 999px;
}
}
}
&.fuse-date-range-end {
&:before {
right: $body-cell-padding;
border-radius: 0 999px 999px 0;
}
&:first-child {
&:before {
left: $body-cell-padding;
border-radius: 999px;
}
}
}
&:first-child {
&:before {
border-radius: 999px 0 0 999px;
}
}
&:last-child {
&:before {
border-radius: 0 999px 999px 0;
}
}
}
.mat-calendar-body-cell-content {
position: relative;
top: 0;
left: 0;
width: 24px;
height: 24px;
font-size: 12px;
}
}
td.mat-calendar-body-label {
+ td.mat-calendar-body-cell {
&.fuse-date-range {
&:before {
border-radius: 999px 0 0 999px;
}
&.fuse-date-range-start {
&.fuse-date-range-end {
border-radius: 999px;
}
}
&.fuse-date-range-end {
&:before {
left: $body-cell-padding;
border-radius: 999px;
}
}
}
}
}
}
}
}
}
}
.time {
width: 100%;
max-width: 196px;
}
}
.start {
align-items: flex-start;
margin-right: 20px;
.month {
.month-label {
margin-left: 8px;
}
}
}
.end {
align-items: flex-end;
margin-left: 20px;
.month {
.month-label {
margin-right: 8px;
}
}
}
}
@@ -0,0 +1,682 @@
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, HostBinding, Input, OnDestroy, OnInit, Output, Renderer2, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Overlay } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { MatCalendarCellCssClasses, MatMonthView } from '@angular/material/datepicker';
import { Subject } from 'rxjs';
import * as moment from 'moment';
import { Moment } from 'moment';
@Component({
selector : 'fuse-date-range',
templateUrl : './date-range.component.html',
styleUrls : ['./date-range.component.scss'],
encapsulation: ViewEncapsulation.None,
exportAs : 'fuseDateRange',
providers : [
{
provide : NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FuseDateRangeComponent),
multi : true
}
]
})
export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnDestroy
{
@Output() readonly rangeChanged: EventEmitter<{ start: string; end: string }> = new EventEmitter<{ start: string; end: string }>();
@ViewChild('matMonthView1') private _matMonthView1: MatMonthView<any>;
@ViewChild('matMonthView2') private _matMonthView2: MatMonthView<any>;
@ViewChild('pickerPanelOrigin', {read: ElementRef}) private _pickerPanelOrigin: ElementRef;
@ViewChild('pickerPanel') private _pickerPanel: TemplateRef<any>;
@HostBinding('class.fuse-date-range') private _defaultClassNames = true;
activeDates: { month1: Moment | null; month2: Moment | null } = {
month1: null,
month2: null
};
setWhichDate: 'start' | 'end' = 'start';
startTimeFormControl: FormControl;
endTimeFormControl: FormControl;
private _dateFormat: string;
private _onChange: (value: any) => void;
private _onTouched: (value: any) => void;
private _programmaticChange!: boolean;
private _range: { start: Moment | null; end: Moment | null } = {
start: null,
end : null
};
private _timeFormat: string;
private _timeRange: boolean;
private readonly _timeRegExp: RegExp = new RegExp('^(0[0-9]|1[0-9]|2[0-4]|[0-9]):([0-5][0-9])(A|(?:AM)|P|(?:PM))?$', 'i');
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _elementRef: ElementRef,
private _overlay: Overlay,
private _renderer2: Renderer2,
private _viewContainerRef: ViewContainerRef
)
{
this._onChange = (): void => {
};
this._onTouched = (): void => {
};
this.dateFormat = 'DD/MM/YYYY';
this.timeFormat = '12';
// Initialize the component
this._init();
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Setter & getter for dateFormat input
*
* @param value
*/
@Input()
set dateFormat(value: string)
{
// Return if the values are the same
if ( this._dateFormat === value )
{
return;
}
// Store the value
this._dateFormat = value;
}
get dateFormat(): string
{
return this._dateFormat;
}
/**
* Setter & getter for timeFormat input
*
* @param value
*/
@Input()
set timeFormat(value: string)
{
// Return if the values are the same
if ( this._timeFormat === value )
{
return;
}
// Set format based on the time format input
this._timeFormat = value === '12' ? 'hh:mmA' : 'HH:mm';
}
get timeFormat(): string
{
return this._timeFormat;
}
/**
* Setter & getter for timeRange input
*
* @param value
*/
@Input()
set timeRange(value: boolean)
{
// Return if the values are the same
if ( this._timeRange === value )
{
return;
}
// Store the value
this._timeRange = value;
// If the time range turned off...
if ( !value )
{
this.range = {
start: this._range.start.clone().startOf('day'),
end : this._range.end.clone().endOf('day')
};
}
}
get timeRange(): boolean
{
return this._timeRange;
}
/**
* Setter & getter for range input
*
* @param value
*/
@Input()
set range(value)
{
if ( !value )
{
return;
}
// Check if the value is an object and has 'start' and 'end' values
if ( !value.start || !value.end )
{
console.error('Range input must have "start" and "end" properties!');
return;
}
// Check if we are setting an individual date or both of them
const whichDate = value.whichDate || null;
// Get the start and end dates as moment
const start = moment(value.start);
const end = moment(value.end);
// If we are only setting the start date...
if ( whichDate === 'start' )
{
// Set the start date
this._range.start = start.clone();
// If the selected start date is after the end date...
if ( this._range.start.isAfter(this._range.end) )
{
// Set the end date to the start date but keep the end date's time
const endDate = start.clone().hours(this._range.end.hours()).minutes(this._range.end.minutes()).seconds(this._range.end.seconds());
// Test this new end date to see if it's ahead of the start date
if ( this._range.start.isBefore(endDate) )
{
// If it's, set the new end date
this._range.end = endDate;
}
else
{
// Otherwise, set the end date same as the start date
this._range.end = start.clone();
}
}
}
// If we are only setting the end date...
if ( whichDate === 'end' )
{
// Set the end date
this._range.end = end.clone();
// If the selected end date is before the start date...
if ( this._range.start.isAfter(this._range.end) )
{
// Set the start date to the end date but keep the start date's time
const startDate = end.clone().hours(this._range.start.hours()).minutes(this._range.start.minutes()).seconds(this._range.start.seconds());
// Test this new end date to see if it's ahead of the start date
if ( this._range.end.isAfter(startDate) )
{
// If it's, set the new start date
this._range.start = startDate;
}
else
{
// Otherwise, set the start date same as the end date
this._range.start = end.clone();
}
}
}
// If we are setting both dates...
if ( !whichDate )
{
// Set the start date
this._range.start = start.clone();
// If the start date is before the end date, set the end date as normal.
// If the start date is after the end date, set the end date same as the start date.
this._range.end = start.isBefore(end) ? end.clone() : start.clone();
}
// Prepare another range object that holds the ISO formatted range dates
const range = {
start: this._range.start.clone().toISOString(),
end : this._range.end.clone().toISOString()
};
// Emit the range changed event with the range
this.rangeChanged.emit(range);
// Update the model with the range if the change was not a programmatic change
// Because programmatic changes trigger writeValue which triggers onChange and onTouched
// internally causing them to trigger twice which breaks the form's pristine and touched
// statuses.
if ( !this._programmaticChange )
{
this._onTouched(range);
this._onChange(range);
}
// Set the active dates
this.activeDates = {
month1: this._range.start.clone(),
month2: this._range.start.clone().add(1, 'month')
};
// Set the time form controls
this.startTimeFormControl.setValue(this._range.start.clone().format(this._timeFormat).toString());
this.endTimeFormControl.setValue(this._range.end.clone().format(this._timeFormat).toString());
// Run ngAfterContentInit on month views to trigger
// re-render on month views if they are available
if ( this._matMonthView1 && this._matMonthView2 )
{
this._matMonthView1.ngAfterContentInit();
this._matMonthView2.ngAfterContentInit();
}
// Reset the programmatic change status
this._programmaticChange = false;
}
get range(): any
{
// Clone the range start and end
const start = this._range.start.clone();
const end = this._range.end.clone();
// Build and return the range object
return {
startDate: start.clone().format(this.dateFormat),
startTime: this.timeRange ? start.clone().format(this.timeFormat) : null,
endDate : end.clone().format(this.dateFormat),
endTime : this.timeRange ? end.clone().format(this.timeFormat) : null
};
}
// -----------------------------------------------------------------------------------------------------
// @ Control Value Accessor
// -----------------------------------------------------------------------------------------------------
/**
* Update the form model on change
*
* @param fn
*/
registerOnChange(fn: any): void
{
this._onChange = fn;
}
/**
* Update the form model on blur
*
* @param fn
*/
registerOnTouched(fn: any): void
{
this._onTouched = fn;
}
/**
* Write to view from model when the form model changes programmatically
*
* @param range
*/
writeValue(range: { start: string; end: string }): void
{
// Set this change as a programmatic one
this._programmaticChange = true;
// Set the range
this.range = range;
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
// @ TODO: Workaround until "angular/issues/20007" resolved
this.writeValue = (): void => {
};
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open the picker panel
*/
openPickerPanel(): void
{
// Create the overlay
const overlayRef = this._overlay.create({
panelClass : 'fuse-date-range-panel',
backdropClass : '',
hasBackdrop : true,
scrollStrategy : this._overlay.scrollStrategies.reposition(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._pickerPanelOrigin)
.withPositions([
{
originX : 'start',
originY : 'bottom',
overlayX: 'start',
overlayY: 'top',
offsetY : 8
},
{
originX : 'start',
originY : 'top',
overlayX: 'start',
overlayY: 'bottom',
offsetY : -8
}
])
});
// Create a portal from the template
const templatePortal = new TemplatePortal(this._pickerPanel, this._viewContainerRef);
// On backdrop click
overlayRef.backdropClick().subscribe(() => {
// If template portal exists and attached...
if ( templatePortal && templatePortal.isAttached )
{
// Detach it
templatePortal.detach();
}
// If overlay exists and attached...
if ( overlayRef && overlayRef.hasAttached() )
{
// Detach it
overlayRef.detach();
overlayRef.dispose();
}
});
// Attach the portal to the overlay
overlayRef.attach(templatePortal);
}
/**
* Get month label
*
* @param month
*/
getMonthLabel(month: number): string
{
if ( month === 1 )
{
return this.activeDates.month1.clone().format('MMMM Y');
}
return this.activeDates.month2.clone().format('MMMM Y');
}
/**
* Date class function to add/remove class names to calendar days
*/
dateClass(): any
{
return (date: Moment): MatCalendarCellCssClasses => {
// If the date is both start and end date...
if ( date.isSame(this._range.start, 'day') && date.isSame(this._range.end, 'day') )
{
return ['fuse-date-range', 'fuse-date-range-start', 'fuse-date-range-end'];
}
// If the date is the start date...
if ( date.isSame(this._range.start, 'day') )
{
return ['fuse-date-range', 'fuse-date-range-start'];
}
// If the date is the end date...
if ( date.isSame(this._range.end, 'day') )
{
return ['fuse-date-range', 'fuse-date-range-end'];
}
// If the date is in between start and end dates...
if ( date.isBetween(this._range.start, this._range.end, 'day') )
{
return ['fuse-date-range', 'fuse-date-range-mid'];
}
return undefined;
};
}
/**
* Date filter to enable/disable calendar days
*/
dateFilter(): any
{
// If we are selecting the end date, disable all the dates that comes before the start date
return (date: Moment): boolean => !(this.setWhichDate === 'end' && date.isBefore(this._range.start, 'day'));
}
/**
* On selected date change
*
* @param date
*/
onSelectedDateChange(date: Moment): void
{
// Create a new range object
const newRange = {
start : this._range.start.clone().toISOString(),
end : this._range.end.clone().toISOString(),
whichDate: null
};
// Replace either the start or the end date with the new one
// depending on which date we are setting
if ( this.setWhichDate === 'start' )
{
newRange.start = moment(newRange.start).year(date.year()).month(date.month()).date(date.date()).toISOString();
}
else
{
newRange.end = moment(newRange.end).year(date.year()).month(date.month()).date(date.date()).toISOString();
}
// Append the which date to the new range object
newRange.whichDate = this.setWhichDate;
// Switch which date to set on the next run
this.setWhichDate = this.setWhichDate === 'start' ? 'end' : 'start';
// Set the range
this.range = newRange;
}
/**
* Go to previous month on both views
*/
prev(): void
{
this.activeDates.month1 = moment(this.activeDates.month1).subtract(1, 'month');
this.activeDates.month2 = moment(this.activeDates.month2).subtract(1, 'month');
}
/**
* Go to next month on both views
*/
next(): void
{
this.activeDates.month1 = moment(this.activeDates.month1).add(1, 'month');
this.activeDates.month2 = moment(this.activeDates.month2).add(1, 'month');
}
/**
* Update the start time
*
* @param event
*/
updateStartTime(event): void
{
// Parse the time
const parsedTime = this._parseTime(event.target.value);
// Go back to the previous value if the form control is not valid
if ( this.startTimeFormControl.invalid )
{
// Override the time
const time = this._range.start.clone().format(this._timeFormat);
// Set the time
this.startTimeFormControl.setValue(time);
// Do not update the range
return;
}
// Append the new time to the start date
const startDate = this._range.start.clone().hours(parsedTime.hours()).minutes(parsedTime.minutes());
// If the new start date is after the current end date,
// use the end date's time and set the start date again
if ( startDate.isAfter(this._range.end) )
{
const endDateHours = this._range.end.hours();
const endDateMinutes = this._range.end.minutes();
// Set the start date
startDate.hours(endDateHours).minutes(endDateMinutes);
}
// If everything is okay, set the new date
this.range = {
start : startDate.toISOString(),
end : this._range.end.clone().toISOString(),
whichDate: 'start'
};
}
/**
* Update the end time
*
* @param event
*/
updateEndTime(event): void
{
// Parse the time
const parsedTime = this._parseTime(event.target.value);
// Go back to the previous value if the form control is not valid
if ( this.endTimeFormControl.invalid )
{
// Override the time
const time = this._range.end.clone().format(this._timeFormat);
// Set the time
this.endTimeFormControl.setValue(time);
// Do not update the range
return;
}
// Append the new time to the end date
const endDate = this._range.end.clone().hours(parsedTime.hours()).minutes(parsedTime.minutes());
// If the new end date is before the current start date,
// use the start date's time and set the end date again
if ( endDate.isBefore(this._range.start) )
{
const startDateHours = this._range.start.hours();
const startDateMinutes = this._range.start.minutes();
// Set the end date
endDate.hours(startDateHours).minutes(startDateMinutes);
}
// If everything is okay, set the new date
this.range = {
start : this._range.start.clone().toISOString(),
end : endDate.toISOString(),
whichDate: 'end'
};
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Initialize
*
* @private
*/
private _init(): void
{
// Start and end time form controls
this.startTimeFormControl = new FormControl('', [Validators.pattern(this._timeRegExp)]);
this.endTimeFormControl = new FormControl('', [Validators.pattern(this._timeRegExp)]);
// Set the default range
this._programmaticChange = true;
this.range = {
start: moment().startOf('day').toISOString(),
end : moment().add(1, 'day').endOf('day').toISOString()
};
// Set the default time range
this._programmaticChange = true;
this.timeRange = true;
}
/**
* Parse the time from the inputs
*
* @param value
* @private
*/
private _parseTime(value: string): Moment
{
// Parse the time using the time regexp
const timeArr = value.split(this._timeRegExp).filter(part => part !== '');
// Get the meridiem
const meridiem = timeArr[2] || null;
// If meridiem exists...
if ( meridiem )
{
// Create a moment using 12-hours format and return it
return moment(value, 'hh:mmA').seconds(0);
}
// If meridiem doesn't exist, create a moment using 24-hours format and return in
return moment(value, 'HH:mm').seconds(0);
}
}
@@ -0,0 +1,32 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMomentDateModule } from '@angular/material-moment-adapter';
import { FuseDateRangeComponent } from '@fuse/components/date-range/date-range.component';
@NgModule({
declarations: [
FuseDateRangeComponent
],
imports : [
CommonModule,
ReactiveFormsModule,
MatButtonModule,
MatDatepickerModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatMomentDateModule
],
exports : [
FuseDateRangeComponent
]
})
export class FuseDateRangeModule
{
}
+1
View File
@@ -0,0 +1 @@
export * from '@fuse/components/date-range/public-api';
@@ -0,0 +1,2 @@
export * from '@fuse/components/date-range/date-range.component';
export * from '@fuse/components/date-range/date-range.module';
@@ -1,83 +0,0 @@
<!-- DEMO CONTENT -->
<div class="demo-content line-height-1.75">
<h1 class="m-0">Early Sunrise in Winter</h1>
<h4 class="mt-0 secondary-text">Demo Content</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tortor nibh, convallis sed purus nec,
auctor venenatis nisl. Suspendisse potenti. Nullam sagittis nulla in diam finibus, sed pharetra velit
vestibulum. Suspendisse euismod in urna eu posuere.
</p>
<blockquote class="my-24">
<p>
Nunc vel lacinia lorem. Nullam tincidunt sed purus eu placerat. Donec id dictum erat. Etiam enim ex, dapibus
et tortor id, posuere pretium est. Maecenas fringilla ipsum vitae neque elementum, at eleifend ante
sollicitudin. Donec viverra augue dolor, a venenatis tellus consectetur sit amet.
</p>
<footer>
John Doe
</footer>
</blockquote>
<p>
Ut ornare sit amet velit vel congue. Ut nec tristique eros. Lorem ipsum dolor sit amet, consectetur
<b>adipiscing elit</b>. Vivamus sed lorem quis nibh porta iaculis. Vestibulum ut eleifend ante, at semper mi.
Nam imperdiet est nisi, quis hendrerit tellus convallis et. Morbi in luctus neque. Curabitur elementum ut est et
gravida. In hac habitasse platea dictumst. In et placerat eros, eu tempor turpis. Curabitur ac felis finibus,
elementum lectus vitae, venenatis est. Integer mollis nisl a eros scelerisque varius. Etiam venenatis lectus vel
erat condimentum tristique vel vel mi. Nulla id euismod mi, et mollis tellus.
</p>
<p>
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur vitae sagittis odio.
Suspendisse ullamcorper nunc non pellentesque laoreet. Curabitur eu tortor id quam pretium mattis. Proin ut quam
velit.
</p>
<img class="mt-24 w-100-p" src="assets/images/demo-content/morain-lake.jpg" style="max-width: 640px">
<p class="mt-8 mb-24 secondary-text">
<em>Nullam tincidunt sed purus eu placerat. Donec id dictum erat. Etiam enim ex, dapibus et tortor id.</em>
</p>
<p>
Quisque sit amet risus enim. Aliquam sit amet interdum justo, at ultricies sapien. Suspendisse et semper urna,
in gravida eros. Quisque id nibh iaculis, euismod urna sed, egestas nisi. Donec eros metus, congue a imperdiet
feugiat, sagittis nec ipsum. Quisque dapibus mollis felis non tristique.
</p>
<p>
Ut auctor, metus sed dapibus tempus, urna diam auctor odio, in malesuada odio risus vitae nisi. Etiam blandit
ante urna, vitae placerat massa mollis in. Duis nec urna ac purus semper dictum ut eget justo. Aenean non
sagittis augue. Sed venenatis rhoncus enim eget ornare. <a href="#">Donec viverra sed felis at venenatis.</a>
Mauris aliquam fringilla nulla, sit amet congue felis dignissim at.
</p>
<ul>
<li>Orci varius</li>
<li>Magnis dis</li>
<li>Conubia nostra</li>
<li>Semper urna</li>
<li>Donec viverra</li>
</ul>
<p>
Quisque accumsan augue tempor ante mollis, sed placerat diam porttitor. Vestibulum dignissim sem vel velit
eleifend, non pellentesque quam convallis. Pellentesque est dolor, dignissim ac tortor tristique, hendrerit
iaculis metus. Praesent pulvinar quam eu leo consectetur faucibus. Vestibulum purus diam, gravida sagittis
feugiat sit amet, tincidunt in ligula. Sed semper vestibulum magna. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Suspendisse tortor nibh, convallis sed purus nec, auctor venenatis nisl. Suspendisse potenti.
</p>
<p>
Nullam sagittis nulla in diam finibus, sed pharetra velit vestibulum. Suspendisse euismod in urna eu posuere.
Etiam blandit nunc arcu, et consectetur orci blandit a. Aliquam condimentum pharetra quam at ultricies. Nunc vel
lacinia lorem. Nullam tincidunt sed purus eu placerat. Donec id dictum erat. Etiam enim ex, dapibus et tortor
id, posuere pretium est. Maecenas fringilla ipsum vitae neque elementum, at eleifend ante sollicitudin. Donec
viverra augue dolor, a venenatis tellus consectetur sit amet.
</p>
</div>
<!-- / DEMO CONTENT -->
@@ -1,16 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-demo-content',
templateUrl: './demo-content.component.html',
styleUrls : ['./demo-content.component.scss']
})
export class FuseDemoContentComponent
{
/**
* Constructor
*/
constructor()
{
}
}
@@ -1,100 +0,0 @@
<div class="demo-sidebar">
<mat-list>
<h3 matSubheader>Sidebar Demo</h3>
<mat-list-item>
<span>Sidebar Item 1</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 2</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 3</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 4</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 5</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 6</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 7</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 8</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 9</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 10</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 11</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 12</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 13</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 14</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 15</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<span>Sidebar Item 16</span>
</mat-list-item>
</mat-list>
</div>
@@ -1,16 +0,0 @@
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()
{
}
}
-27
View File
@@ -1,27 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { MatDividerModule, MatListModule } from '@angular/material';
import { FuseDemoContentComponent } from './demo-content/demo-content.component';
import { FuseDemoSidebarComponent } from './demo-sidebar/demo-sidebar.component';
@NgModule({
declarations: [
FuseDemoContentComponent,
FuseDemoSidebarComponent
],
imports : [
RouterModule,
MatDividerModule,
MatListModule
],
exports : [
FuseDemoContentComponent,
FuseDemoSidebarComponent
]
})
export class FuseDemoModule
{
}
@@ -0,0 +1,3 @@
<div class="fuse-drawer-content">
<ng-content></ng-content>
</div>
@@ -0,0 +1,131 @@
/* Variables */
$fuse-drawer-width: 320;
fuse-drawer {
position: relative;
display: flex;
flex-direction: column;
flex: 1 1 auto;
width: #{$fuse-drawer-width}px;
min-width: #{$fuse-drawer-width}px;
max-width: #{$fuse-drawer-width}px;
z-index: 300;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .35);
@apply bg-card;
/* Animations */
&.fuse-drawer-animations-enabled {
transition-duration: 400ms;
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
transition-property: visibility, margin-left, margin-right, transform, width, max-width, min-width;
.fuse-drawer-content {
transition-duration: 400ms;
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
transition-property: width, max-width, min-width;
}
}
/* Over mode */
&.fuse-drawer-mode-over {
position: absolute;
top: 0;
bottom: 0;
/* Fixed mode */
&.fuse-drawer-fixed {
position: fixed;
}
}
/* Left position */
&.fuse-drawer-position-left {
/* Side mode */
&.fuse-drawer-mode-side {
margin-left: -#{$fuse-drawer-width}px;
&.fuse-drawer-opened {
margin-left: 0;
}
}
/* Over mode */
&.fuse-drawer-mode-over {
left: 0;
transform: translate3d(-100%, 0, 0);
&.fuse-drawer-opened {
transform: translate3d(0, 0, 0);
}
}
/* Content */
.fuse-drawer-content {
left: 0;
}
}
/* Right position */
&.fuse-drawer-position-right {
/* Side mode */
&.fuse-drawer-mode-side {
margin-right: -#{$fuse-drawer-width}px;
&.fuse-drawer-opened {
margin-right: 0;
}
}
/* Over mode */
&.fuse-drawer-mode-over {
right: 0;
transform: translate3d(100%, 0, 0);
&.fuse-drawer-opened {
transform: translate3d(0, 0, 0);
}
}
/* Content */
.fuse-drawer-content {
right: 0;
}
}
/* Content */
.fuse-drawer-content {
position: absolute;
display: flex;
flex: 1 1 auto;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow: hidden;
@apply bg-card;
}
}
/* Overlay */
.fuse-drawer-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 299;
opacity: 1;
background-color: rgba(0, 0, 0, 0.6);
/* Fixed mode */
&.fuse-drawer-overlay-fixed {
position: fixed;
}
/* Transparent overlay */
&.fuse-drawer-overlay-transparent {
background-color: transparent;
}
}
@@ -0,0 +1,437 @@
import { Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, Renderer2, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
import { FuseDrawerMode, FuseDrawerPosition } from '@fuse/components/drawer/drawer.types';
import { FuseDrawerService } from '@fuse/components/drawer/drawer.service';
import { FuseUtilsService } from '@fuse/services/utils/utils.service';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
@Component({
selector : 'fuse-drawer',
templateUrl : './drawer.component.html',
styleUrls : ['./drawer.component.scss'],
encapsulation: ViewEncapsulation.None,
exportAs : 'fuseDrawer'
})
export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_fixed: BooleanInput;
static ngAcceptInputType_opened: BooleanInput;
static ngAcceptInputType_transparentOverlay: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() fixed: boolean = false;
@Input() mode: FuseDrawerMode = 'side';
@Input() name: string = this._fuseUtilsService.randomId();
@Input() opened: boolean = false;
@Input() position: FuseDrawerPosition = 'left';
@Input() transparentOverlay: boolean = false;
@Output() readonly fixedChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
@Output() readonly modeChanged: EventEmitter<FuseDrawerMode> = new EventEmitter<FuseDrawerMode>();
@Output() readonly openedChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
@Output() readonly positionChanged: EventEmitter<FuseDrawerPosition> = new EventEmitter<FuseDrawerPosition>();
private _animationsEnabled: boolean = false;
private _hovered: boolean = false;
private _overlay: HTMLElement;
private _player: AnimationPlayer;
/**
* Constructor
*/
constructor(
private _animationBuilder: AnimationBuilder,
private _elementRef: ElementRef,
private _renderer2: Renderer2,
private _fuseDrawerService: FuseDrawerService,
private _fuseUtilsService: FuseUtilsService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Host binding for component classes
*/
@HostBinding('class') get classList(): any
{
return {
'fuse-drawer-animations-enabled' : this._animationsEnabled,
'fuse-drawer-fixed' : this.fixed,
'fuse-drawer-hover' : this._hovered,
[`fuse-drawer-mode-${this.mode}`] : true,
'fuse-drawer-opened' : this.opened,
[`fuse-drawer-position-${this.position}`]: true
};
}
/**
* Host binding for component inline styles
*/
@HostBinding('style') get styleList(): any
{
return {
'visibility': this.opened ? 'visible' : 'hidden'
};
}
// -----------------------------------------------------------------------------------------------------
// @ Decorated methods
// -----------------------------------------------------------------------------------------------------
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Fixed
if ( 'fixed' in changes )
{
// Coerce the value to a boolean
this.fixed = coerceBooleanProperty(changes.fixed.currentValue);
// Execute the observable
this.fixedChanged.next(this.fixed);
}
// Mode
if ( 'mode' in changes )
{
// Get the previous and current values
const previousMode = changes.mode.previousValue;
const currentMode = changes.mode.currentValue;
// Disable the animations
this._disableAnimations();
// If the mode changes: 'over -> side'
if ( previousMode === 'over' && currentMode === 'side' )
{
// Hide the overlay
this._hideOverlay();
}
// If the mode changes: 'side -> over'
if ( previousMode === 'side' && currentMode === 'over' )
{
// If the drawer is opened
if ( this.opened )
{
// Show the overlay
this._showOverlay();
}
}
// Execute the observable
this.modeChanged.next(currentMode);
// Enable the animations after a delay
// The delay must be bigger than the current transition-duration
// to make sure nothing will be animated while the mode is changing
setTimeout(() => {
this._enableAnimations();
}, 500);
}
// Opened
if ( 'opened' in changes )
{
// Coerce the value to a boolean
const open = coerceBooleanProperty(changes.opened.currentValue);
// Open/close the drawer
this._toggleOpened(open);
}
// Position
if ( 'position' in changes )
{
// Execute the observable
this.positionChanged.next(this.position);
}
// Transparent overlay
if ( 'transparentOverlay' in changes )
{
// Coerce the value to a boolean
this.transparentOverlay = coerceBooleanProperty(changes.transparentOverlay.currentValue);
}
}
/**
* On init
*/
ngOnInit(): void
{
// Register the drawer
this._fuseDrawerService.registerComponent(this.name, this);
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Finish the animation
if ( this._player )
{
this._player.finish();
}
// Deregister the drawer from the registry
this._fuseDrawerService.deregisterComponent(this.name);
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open the drawer
*/
open(): void
{
// Return if the drawer has already opened
if ( this.opened )
{
return;
}
// Open the drawer
this._toggleOpened(true);
}
/**
* Close the drawer
*/
close(): void
{
// Return if the drawer has already closed
if ( !this.opened )
{
return;
}
// Close the drawer
this._toggleOpened(false);
}
/**
* Toggle the drawer
*/
toggle(): void
{
if ( this.opened )
{
this.close();
}
else
{
this.open();
}
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Enable the animations
*
* @private
*/
private _enableAnimations(): void
{
// Return if the animations are already enabled
if ( this._animationsEnabled )
{
return;
}
// Enable the animations
this._animationsEnabled = true;
}
/**
* Disable the animations
*
* @private
*/
private _disableAnimations(): void
{
// Return if the animations are already disabled
if ( !this._animationsEnabled )
{
return;
}
// Disable the animations
this._animationsEnabled = false;
}
/**
* Show the backdrop
*
* @private
*/
private _showOverlay(): void
{
// Create the backdrop element
this._overlay = this._renderer2.createElement('div');
// Return if overlay couldn't be create for some reason
if ( !this._overlay )
{
return;
}
// Add a class to the backdrop element
this._overlay.classList.add('fuse-drawer-overlay');
// Add a class depending on the fixed option
if ( this.fixed )
{
this._overlay.classList.add('fuse-drawer-overlay-fixed');
}
// Add a class depending on the transparentOverlay option
if ( this.transparentOverlay )
{
this._overlay.classList.add('fuse-drawer-overlay-transparent');
}
// Append the backdrop to the parent of the drawer
this._renderer2.appendChild(this._elementRef.nativeElement.parentElement, this._overlay);
// Create the enter animation and attach it to the player
this._player = this._animationBuilder.build([
style({opacity: 0}),
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1}))
]).create(this._overlay);
// Once the animation is done...
this._player.onDone(() => {
// Destroy the player
this._player.destroy();
this._player = null;
});
// Play the animation
this._player.play();
// Add an event listener to the overlay
this._overlay.addEventListener('click', () => {
this.close();
});
}
/**
* Hide the backdrop
*
* @private
*/
private _hideOverlay(): void
{
if ( !this._overlay )
{
return;
}
// Create the leave animation and attach it to the player
this._player = this._animationBuilder.build([
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 0}))
]).create(this._overlay);
// Play the animation
this._player.play();
// Once the animation is done...
this._player.onDone(() => {
// Destroy the player
this._player.destroy();
this._player = null;
// If the backdrop still exists...
if ( this._overlay )
{
// Remove the backdrop
this._overlay.parentNode.removeChild(this._overlay);
this._overlay = null;
}
});
}
/**
* Open/close the drawer
*
* @param open
* @private
*/
private _toggleOpened(open: boolean): void
{
// Set the opened
this.opened = open;
// Enable the animations
this._enableAnimations();
// If the mode is 'over'
if ( this.mode === 'over' )
{
// If the drawer opens, show the overlay
if ( open )
{
this._showOverlay();
}
// Otherwise, close the overlay
else
{
this._hideOverlay();
}
}
// Execute the observable
this.openedChanged.next(open);
}
}
@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FuseDrawerComponent } from '@fuse/components/drawer/drawer.component';
@NgModule({
declarations: [
FuseDrawerComponent
],
imports : [
CommonModule
],
exports : [
FuseDrawerComponent
]
})
export class FuseDrawerModule
{
}
@@ -0,0 +1,52 @@
import { Injectable } from '@angular/core';
import { FuseDrawerComponent } from '@fuse/components/drawer/drawer.component';
@Injectable({
providedIn: 'root'
})
export class FuseDrawerService
{
private _componentRegistry: Map<string, FuseDrawerComponent> = new Map<string, FuseDrawerComponent>();
/**
* Constructor
*/
constructor()
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Register drawer component
*
* @param name
* @param component
*/
registerComponent(name: string, component: FuseDrawerComponent): void
{
this._componentRegistry.set(name, component);
}
/**
* Deregister drawer component
*
* @param name
*/
deregisterComponent(name: string): void
{
this._componentRegistry.delete(name);
}
/**
* Get drawer component from the registry
*
* @param name
*/
getComponent(name: string): FuseDrawerComponent | undefined
{
return this._componentRegistry.get(name);
}
}
@@ -0,0 +1,7 @@
export type FuseDrawerMode =
| 'over'
| 'side';
export type FuseDrawerPosition =
| 'left'
| 'right';
+1
View File
@@ -0,0 +1 @@
export * from '@fuse/components/drawer/public-api';
@@ -0,0 +1,4 @@
export * from '@fuse/components/drawer/drawer.component';
export * from '@fuse/components/drawer/drawer.module';
export * from '@fuse/components/drawer/drawer.service';
export * from '@fuse/components/drawer/drawer.types';
@@ -0,0 +1,12 @@
<!-- Button -->
<button
mat-icon-button
[matTooltip]="tooltip || 'Toggle Fullscreen'"
(click)="toggleFullscreen()">
<ng-container [ngTemplateOutlet]="iconTpl || defaultIconTpl"></ng-container>
</button>
<!-- Default icon -->
<ng-template #defaultIconTpl>
<mat-icon [svgIcon]="'heroicons_outline:arrows-expand'"></mat-icon>
</ng-template>
@@ -0,0 +1,166 @@
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit, TemplateRef, ViewEncapsulation } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { FSDocument, FSDocumentElement } from '@fuse/components/fullscreen/fullscreen.types';
@Component({
selector : 'fuse-fullscreen',
templateUrl : './fullscreen.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'fuseFullscreen'
})
export class FuseFullscreenComponent implements OnInit
{
@Input() iconTpl: TemplateRef<any>;
@Input() tooltip: string;
private _fsDoc: FSDocument;
private _fsDocEl: FSDocumentElement;
private _isFullscreen: boolean = false;
/**
* Constructor
*/
constructor(@Inject(DOCUMENT) private _document: Document)
{
this._fsDoc = _document as FSDocument;
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
this._fsDocEl = document.documentElement as FSDocumentElement;
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle the fullscreen mode
*/
toggleFullscreen(): void
{
// Check if the fullscreen is open
this._isFullscreen = this._getBrowserFullscreenElement() !== null;
// Toggle the fullscreen
if ( this._isFullscreen )
{
this._closeFullscreen();
}
else
{
this._openFullscreen();
}
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Get browser's fullscreen element
*
* @private
*/
private _getBrowserFullscreenElement(): Element
{
if ( typeof this._fsDoc.fullscreenElement !== 'undefined' )
{
return this._fsDoc.fullscreenElement;
}
if ( typeof this._fsDoc.mozFullScreenElement !== 'undefined' )
{
return this._fsDoc.mozFullScreenElement;
}
if ( typeof this._fsDoc.msFullscreenElement !== 'undefined' )
{
return this._fsDoc.msFullscreenElement;
}
if ( typeof this._fsDoc.webkitFullscreenElement !== 'undefined' )
{
return this._fsDoc.webkitFullscreenElement;
}
throw new Error('Fullscreen mode is not supported by this browser');
}
/**
* Open the fullscreen
*
* @private
*/
private _openFullscreen(): void
{
if ( this._fsDocEl.requestFullscreen )
{
this._fsDocEl.requestFullscreen();
return;
}
// Firefox
if ( this._fsDocEl.mozRequestFullScreen )
{
this._fsDocEl.mozRequestFullScreen();
return;
}
// Chrome, Safari and Opera
if ( this._fsDocEl.webkitRequestFullscreen )
{
this._fsDocEl.webkitRequestFullscreen();
return;
}
// IE/Edge
if ( this._fsDocEl.msRequestFullscreen )
{
this._fsDocEl.msRequestFullscreen();
return;
}
}
/**
* Close the fullscreen
*
* @private
*/
private _closeFullscreen(): void
{
if ( this._fsDoc.exitFullscreen )
{
this._fsDoc.exitFullscreen();
return;
}
// Firefox
if ( this._fsDoc.mozCancelFullScreen )
{
this._fsDoc.mozCancelFullScreen();
return;
}
// Chrome, Safari and Opera
if ( this._fsDoc.webkitExitFullscreen )
{
this._fsDoc.webkitExitFullscreen();
return;
}
// IE/Edge
else if ( this._fsDoc.msExitFullscreen )
{
this._fsDoc.msExitFullscreen();
return;
}
}
}
@@ -0,0 +1,24 @@
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen/fullscreen.component';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [
FuseFullscreenComponent
],
imports: [
MatButtonModule,
MatIconModule,
MatTooltipModule,
CommonModule
],
exports : [
FuseFullscreenComponent
]
})
export class FuseFullscreenModule
{
}
@@ -0,0 +1,16 @@
export interface FSDocument extends HTMLDocument
{
mozFullScreenElement?: Element;
mozCancelFullScreen?: () => void;
msFullscreenElement?: Element;
msExitFullscreen?: () => void;
webkitFullscreenElement?: Element;
webkitExitFullscreen?: () => void;
}
export interface FSDocumentElement extends HTMLElement
{
mozRequestFullScreen?: () => void;
msRequestFullscreen?: () => void;
webkitRequestFullscreen?: () => void;
}
+1
View File
@@ -0,0 +1 @@
export * from '@fuse/components/fullscreen/public-api';
@@ -0,0 +1,3 @@
export * from '@fuse/components/fullscreen/fullscreen.component';
export * from '@fuse/components/fullscreen/fullscreen.module';
export * from '@fuse/components/fullscreen/fullscreen.types';
@@ -0,0 +1,9 @@
<ng-content></ng-content>
<!-- @formatter:off -->
<ng-template let-highlightedCode="highlightedCode" let-lang="lang">
<div class="fuse-highlight fuse-highlight-code-container">
<pre [ngClass]="'language-' + lang"><code [ngClass]="'language-' + lang" [innerHTML]="highlightedCode"></code></pre>
</div>
</ng-template>
<!-- @formatter:on -->
@@ -1,9 +1,3 @@
:host {
display: block;
width: 100%;
padding: 8px;
background: #263238;
cursor: text;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
textarea[fuse-highlight] {
display: none;
}
@@ -1,45 +1,36 @@
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 '@fuse/components/highlight/prism-languages';
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EmbeddedViewRef, Input, OnChanges, Renderer2, SecurityContext, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { FuseHighlightService } from '@fuse/components/highlight/highlight.service';
@Component({
selector : 'fuse-highlight',
template : '',
styleUrls: ['./highlight.component.scss']
selector : 'textarea[fuse-highlight]',
templateUrl : './highlight.component.html',
styleUrls : ['./highlight.component.scss'],
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'fuseHighlight'
})
export class FuseHighlightComponent implements OnInit, OnDestroy
export class FuseHighlightComponent implements OnChanges, AfterViewInit
{
// Source
@ContentChild('source')
source: ElementRef;
@Input() code: string;
@Input() lang: string;
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
// Lang
@Input('lang')
lang: string;
// Path
@Input('path')
path: string;
// Private
private _unsubscribeAll: Subject<any>;
highlightedCode: string;
private _viewRef: EmbeddedViewRef<any>;
/**
* Constructor
*
* @param {ElementRef} _elementRef
* @param {HttpClient} _httpClient
*/
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _domSanitizer: DomSanitizer,
private _elementRef: ElementRef,
private _httpClient: HttpClient
private _renderer2: Renderer2,
private _fuseHighlightService: FuseHighlightService,
private _viewContainerRef: ViewContainerRef
)
{
// Set the private defaults
this._unsubscribeAll = new Subject();
}
// -----------------------------------------------------------------------------------------------------
@@ -47,103 +38,95 @@ export class FuseHighlightComponent implements OnInit, OnDestroy
// -----------------------------------------------------------------------------------------------------
/**
* On init
* On changes
*
* @param changes
*/
ngOnInit(): void
ngOnChanges(changes: SimpleChanges): void
{
// If there is no language defined, return...
// Code & Lang
if ( 'code' in changes || 'lang' in changes )
{
// Return if the viewContainerRef is not available
if ( !this._viewContainerRef.length )
{
return;
}
// Highlight and insert the code
this._highlightAndInsert();
}
}
/**
* After view init
*/
ngAfterViewInit(): void
{
// Return if there is no language set
if ( !this.lang )
{
return;
}
// If the path is defined...
if ( this.path )
// If there is no code input, get the code from
// the textarea
if ( !this.code )
{
// Get the source
this._httpClient.get(this.path, {responseType: 'text'})
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((response) => {
// Highlight it
this.highlight(response);
});
// Get the code
this.code = this._elementRef.nativeElement.value;
}
// If the path is not defined and the source element exists...
if ( !this.path && this.source )
{
// Highlight it
this.highlight(this.source.nativeElement.value);
}
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
// Highlight and insert
this._highlightAndInsert();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Highlight the given source code
* Highlight and insert the highlighted code
*
* @param sourceCode
* @private
*/
highlight(sourceCode): void
private _highlightAndInsert(): void
{
// Split the source into lines
const sourceLines = sourceCode.split('\n');
// Remove the first and the last line of the source
// code if they are blank lines. This way, the html
// can be formatted properly while using fuse-highlight
// component
if ( !sourceLines[0].trim() )
// Return if the template reference is not available
if ( !this.templateRef )
{
sourceLines.shift();
return;
}
if ( !sourceLines[sourceLines.length - 1].trim() )
// Return if the code or language is not defined
if ( !this.code || !this.lang )
{
sourceLines.pop();
return;
}
// Find the first non-whitespace char index in
// the first line of the source code
const indexOfFirstChar = sourceLines[0].search(/\S|$/);
// Destroy the component if there is already one
if ( this._viewRef )
{
this._viewRef.destroy();
this._viewRef = null;
}
// Generate the trimmed source
let source = '';
// Highlight and sanitize the code just in case
this.highlightedCode = this._domSanitizer.sanitize(SecurityContext.HTML, this._fuseHighlightService.highlight(this.code, this.lang));
// Iterate through all the lines
sourceLines.forEach((line, index) => {
// Return if the highlighted code is null
if ( this.highlightedCode === null )
{
return;
}
// Trim the beginning white space depending on the index
// and concat the source code
source = source + line.substr(indexOfFirstChar, line.length);
// If it's not the last line...
if ( index !== sourceLines.length - 1 )
{
// Add a line break at the end
source = source + '\n';
}
// Render and insert the template
this._viewRef = this._viewContainerRef.createEmbeddedView(this.templateRef, {
highlightedCode: this.highlightedCode,
lang : this.lang
});
// Generate the highlighted code
const highlightedCode = Prism.highlight(source, Prism.languages[this.lang]);
// Replace the innerHTML of the component with the highlighted code
this._elementRef.nativeElement.innerHTML =
'<pre><code class="highlight language-' + this.lang + '">' + highlightedCode + '</code></pre>';
// Detect the changes
this._viewRef.detectChanges();
}
}
@@ -1,14 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FuseHighlightComponent } from '@fuse/components/highlight/highlight.component';
@NgModule({
declarations: [
FuseHighlightComponent
],
exports: [
FuseHighlightComponent
imports : [
CommonModule
],
exports : [
FuseHighlightComponent
]
})
export class FuseHighlightModule
{
@@ -0,0 +1,82 @@
import { Injectable } from '@angular/core';
import hljs from 'highlight.js';
@Injectable({
providedIn: 'root'
})
export class FuseHighlightService
{
/**
* Constructor
*/
constructor()
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Highlight
*/
highlight(code: string, language: string): string
{
// Format the code
code = this._format(code);
// Highlight and return the code
return hljs.highlight(code, {language}).value;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Remove the empty lines around the code block
* and re-align the indentation based on the first
* non-whitespace indented character
*
* @param code
* @private
*/
private _format(code: string): string
{
let indentation = 0;
// Split the code into lines and store the lines
const lines = code.split('\n');
// Trim the empty lines around the code block
while ( lines.length && lines[0].trim() === '' )
{
lines.shift();
}
while ( lines.length && lines[lines.length - 1].trim() === '' )
{
lines.pop();
}
// Iterate through the lines
lines.filter(line => line.length)
.forEach((line, index) => {
// Always get the indentation of the first line so we can
// have something to compare with
if ( index === 0 )
{
indentation = line.search(/\S|$/);
return;
}
// Look at all the remaining lines to figure out the smallest indentation.
indentation = Math.min(line.search(/\S|$/), indentation);
});
// Iterate through the lines one more time, remove the extra
// indentation, join them together and return it
return lines.map(line => line.substring(indentation)).join('\n');
}
}
+1
View File
@@ -0,0 +1 @@
export * from '@fuse/components/highlight/public-api';
@@ -1,18 +0,0 @@
import 'prismjs/prism';
import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-c';
import 'prismjs/components/prism-cpp';
import 'prismjs/components/prism-csharp';
import 'prismjs/components/prism-css';
import 'prismjs/components/prism-diff';
import 'prismjs/components/prism-markup';
import 'prismjs/components/prism-markup-templating';
import 'prismjs/components/prism-java';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-perl';
import 'prismjs/components/prism-php';
import 'prismjs/components/prism-python';
import 'prismjs/components/prism-sass';
import 'prismjs/components/prism-scss';
import 'prismjs/components/prism-typescript';
@@ -0,0 +1,3 @@
export * from '@fuse/components/highlight/highlight.component';
export * from '@fuse/components/highlight/highlight.module';
export * from '@fuse/components/highlight/highlight.service';
-12
View File
@@ -1,12 +0,0 @@
export * from './confirm-dialog/confirm-dialog.module';
export * from './countdown/countdown.module';
export * from './demo/demo.module';
export * from './highlight/highlight.module';
export * from './material-color-picker/material-color-picker.module';
export * from './navigation/navigation.module';
export * from './progress-bar/progress-bar.module';
export * from './search-bar/search-bar.module';
export * from './shortcuts/shortcuts.module';
export * from './sidebar/sidebar.module';
export * from './theme-options/theme-options.module';
export * from './widget/widget.module';
+1
View File
@@ -0,0 +1 @@
export * from '@fuse/components/masonry/public-api';
@@ -0,0 +1,3 @@
<div class="flex">
<ng-container *ngTemplateOutlet="columnsTemplate; context: { $implicit: distributedColumns }"></ng-container>
</div>
@@ -0,0 +1,87 @@
import { AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core';
import { fuseAnimations } from '@fuse/animations';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
@Component({
selector : 'fuse-masonry',
templateUrl : './masonry.component.html',
styleUrls : ['./masonry.component.scss'],
encapsulation: ViewEncapsulation.None,
animations : fuseAnimations,
exportAs : 'fuseMasonry'
})
export class FuseMasonryComponent implements OnChanges, AfterViewInit
{
@Input() columnsTemplate: TemplateRef<any>;
@Input() columns: number;
@Input() items: any[] = [];
distributedColumns: any[] = [];
/**
* Constructor
*/
constructor(private _fuseMediaWatcherService: FuseMediaWatcherService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Columns
if ( 'columns' in changes )
{
// Distribute the items
this._distributeItems();
}
// Items
if ( 'items' in changes )
{
// Distribute the items
this._distributeItems();
}
}
/**
* After view init
*/
ngAfterViewInit(): void
{
// Distribute the items for the first time
this._distributeItems();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Distribute items into columns
*/
private _distributeItems(): void
{
// Return an empty array if there are no items
if ( this.items.length === 0 )
{
this.distributedColumns = [];
return;
}
// Prepare the distributed columns array
this.distributedColumns = Array.from(Array(this.columns), item => ({items: []}));
// Distribute the items to columns
for ( let i = 0; i < this.items.length; i++ )
{
this.distributedColumns[i % this.columns].items.push(this.items[i]);
}
}
}
@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FuseMasonryComponent } from '@fuse/components/masonry/masonry.component';
@NgModule({
declarations: [
FuseMasonryComponent
],
imports : [
CommonModule
],
exports : [
FuseMasonryComponent
]
})
export class FuseMasonryModule
{
}
@@ -0,0 +1,2 @@
export * from '@fuse/components/masonry/masonry.component';
export * from '@fuse/components/masonry/masonry.module';
@@ -1,65 +0,0 @@
<button mat-icon-button
class="mat-elevation-z1"
[matMenuTriggerFor]="colorMenu"
(menuOpened)="onMenuOpen()"
[ngClass]="selectedPalette + '-' + selectedHue">
<mat-icon>palette</mat-icon>
</button>
<mat-menu #colorMenu="matMenu" class="fuse-material-color-picker-menu mat-elevation-z8">
<header [ngClass]="selectedColor?.class || 'accent'" class="mat-elevation-z4"
fxLayout="row" fxLayoutAlign="space-between center">
<button mat-icon-button
class="secondary-text"
[style.visibility]="view === 'hues' ? 'visible' : 'hidden'"
(click)="goToPalettesView($event)" aria-label="Palette">
<mat-icon class="s-20">arrow_back</mat-icon>
</button>
<span *ngIf="selectedColor?.palette">
{{selectedColor.palette}} {{selectedColor.hue}}
</span>
<span *ngIf="!selectedColor?.palette">
Select a Color
</span>
<button mat-icon-button
class="remove-color-button secondary-text"
(click)="removeColor($event)"
aria-label="Remove color"
matTooltip="Remove color">
<mat-icon class="s-20">delete</mat-icon>
</button>
</header>
<div [ngSwitch]="view" class="views">
<div class="view" *ngSwitchCase="'palettes'">
<div fxLayout="row wrap" fxLayoutAlign="start start" class="colors" fusePerfectScrollbar>
<div class="color" fxLayout="row" fxLayoutAlign="center center"
*ngFor="let color of (colors | keys)"
[ngClass]="color.key"
[class.selected]="selectedPalette === color.key"
(click)="selectPalette($event, color.key)">
</div>
</div>
</div>
<div class="view" *ngSwitchCase="'hues'">
<div fxLayout="row wrap" fxLayoutAlign="start start" class="colors" fusePerfectScrollbar>
<div class="color" fxLayout="row" fxLayoutAlign="center center"
*ngFor="let hue of hues"
[fxHide]="selectedPalette === 'fuse-white' && hue !== '500' || selectedPalette === 'fuse-black' && hue !== '500'"
[ngClass]="selectedPalette + '-' + hue"
[class.selected]="selectedHue === hue"
(click)="selectHue($event, hue)">
</div>
</div>
</div>
</div>
</mat-menu>
@@ -1,39 +0,0 @@
.fuse-material-color-picker-menu {
width: 245px;
.mat-menu-content {
padding: 0;
.views {
display: flex;
flex-direction: column;
min-height: 165px;
.view {
overflow: hidden;
.colors {
padding: 1px 0 0 0;
margin-left: -1px;
.color {
width: 40px;
height: 40px;
margin: 0 0 1px 1px;
border-radius: 0;
cursor: pointer;
transition: border-radius .4s cubic-bezier(.25, .8, .25, 1);
&:hover {
border-radius: 20%;
}
&.selected {
border-radius: 50% !important;
}
}
}
}
}
}
}
@@ -1,261 +0,0 @@
import { Component, EventEmitter, forwardRef, Input, Output, ViewEncapsulation } from '@angular/core';
import { fuseAnimations } from '@fuse/animations';
import { MatColors } from '@fuse/mat-colors';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export const FUSE_MATERIAL_COLOR_PICKER_VALUE_ACCESSOR: any = {
provide : NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FuseMaterialColorPickerComponent),
multi : true
};
@Component({
selector : 'fuse-material-color-picker',
templateUrl : './material-color-picker.component.html',
styleUrls : ['./material-color-picker.component.scss'],
animations : fuseAnimations,
encapsulation: ViewEncapsulation.None,
providers : [FUSE_MATERIAL_COLOR_PICKER_VALUE_ACCESSOR]
})
export class FuseMaterialColorPickerComponent implements ControlValueAccessor
{
colors: any;
hues: string[];
view: string;
selectedColor: any;
selectedPalette: string;
selectedHue: string;
// Color changed
@Output()
colorChanged: EventEmitter<any>;
// Private
private _color: string;
private _modelChange: (value: any) => void;
private _modelTouched: (value: any) => void;
/**
* Constructor
*/
constructor()
{
// Set the defaults
this.colorChanged = new EventEmitter();
this.colors = MatColors.all;
this.hues = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', 'A100', 'A200', 'A400', 'A700'];
this.selectedHue = '500';
this.view = 'palettes';
// Set the private defaults
this._color = '';
this._modelChange = () => {
};
this._modelTouched = () => {
};
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Selected class
*
* @param value
*/
@Input()
set color(value)
{
if ( !value || value === '' || this._color === value )
{
return;
}
// Split the color value (red-400, blue-500, fuse-navy-700 etc.)
const colorParts = value.split('-');
// Take the very last part as the selected hue value
this.selectedHue = colorParts[colorParts.length - 1];
// Remove the last part
colorParts.pop();
// Rejoin the remaining parts as the selected palette name
this.selectedPalette = colorParts.join('-');
// Store the color value
this._color = value;
}
get color(): string
{
return this._color;
}
// -----------------------------------------------------------------------------------------------------
// @ Control Value Accessor implementation
// -----------------------------------------------------------------------------------------------------
/**
* Register on change function
*
* @param fn
*/
registerOnChange(fn: any): void
{
this._modelChange = fn;
}
/**
* Register on touched function
*
* @param fn
*/
registerOnTouched(fn: any): void
{
this._modelTouched = fn;
}
/**
* Write value to the view from model
*
* @param color
*/
writeValue(color: any): void
{
// Return if null
if ( !color )
{
return;
}
// Set the color
this.color = color;
// Update the selected color
this.updateSelectedColor();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Select palette
*
* @param event
* @param palette
*/
selectPalette(event, palette): void
{
// Stop propagation
event.stopPropagation();
// Go to 'hues' view
this.view = 'hues';
// Update the selected palette
this.selectedPalette = palette;
// Update the selected color
this.updateSelectedColor();
}
/**
* Select hue
*
* @param event
* @param hue
*/
selectHue(event, hue): void
{
// Stop propagation
event.stopPropagation();
// Update the selected huse
this.selectedHue = hue;
// Update the selected color
this.updateSelectedColor();
}
/**
* Remove color
*
* @param event
*/
removeColor(event): void
{
// Stop propagation
event.stopPropagation();
// Return to the 'palettes' view
this.view = 'palettes';
// Clear the selected palette and hue
this.selectedPalette = '';
this.selectedHue = '';
// Update the selected color
this.updateSelectedColor();
}
/**
* Update selected color
*/
updateSelectedColor(): void
{
if ( this.selectedColor && this.selectedColor.palette === this.selectedPalette && this.selectedColor.hue === this.selectedHue )
{
return;
}
// Set the selected color object
this.selectedColor = {
palette: this.selectedPalette,
hue : this.selectedHue,
class : this.selectedPalette + '-' + this.selectedHue,
bg : this.selectedPalette === '' ? '' : MatColors.getColor(this.selectedPalette)[this.selectedHue],
fg : this.selectedPalette === '' ? '' : MatColors.getColor(this.selectedPalette).contrast[this.selectedHue]
};
// Emit the color changed event
this.colorChanged.emit(this.selectedColor);
// Mark the model as touched
this._modelTouched(this.selectedColor.class);
// Update the model
this._modelChange(this.selectedColor.class);
}
/**
* Go to palettes view
*
* @param event
*/
goToPalettesView(event): void
{
// Stop propagation
event.stopPropagation();
this.view = 'palettes';
}
/**
* On menu open
*/
onMenuOpen(): void
{
if ( this.selectedPalette === '' )
{
this.view = 'palettes';
}
else
{
this.view = 'hues';
}
}
}
@@ -1,32 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule, MatIconModule, MatMenuModule, MatTooltipModule } from '@angular/material';
import { FusePipesModule } from '@fuse/pipes/pipes.module';
import { FuseMaterialColorPickerComponent } from '@fuse/components/material-color-picker/material-color-picker.component';
@NgModule({
declarations: [
FuseMaterialColorPickerComponent
],
imports: [
CommonModule,
FlexLayoutModule,
MatButtonModule,
MatIconModule,
MatMenuModule,
MatTooltipModule,
FusePipesModule
],
exports: [
FuseMaterialColorPickerComponent
],
})
export class FuseMaterialColorPickerModule
{
}
@@ -1,14 +0,0 @@
@mixin fuse-material-color-picker-theme($theme) {
$background: map-get($theme, background);
.fuse-material-color-picker-menu {
.mat-menu-content {
.views {
background: #303030;
}
}
}
}
@@ -1,69 +0,0 @@
<ng-container *ngIf="!item.hidden">
<!-- normal collapse -->
<a class="nav-link" [ngClass]="item.classes" *ngIf="!item.url && !item.function">
<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', 'accent']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
[target]="item.openInNewTab ? '_blank' : '_self'">
<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'">
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
</a>
<!-- item.function -->
<span class="nav-link" [ngClass]="item.classes" *ngIf="!item.url && item.function"
(click)="item.function()">
<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', 'accent']"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}">
<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'">
<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.primaryBackground}}">
<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>
@@ -1,86 +0,0 @@
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;
}
}
@@ -0,0 +1,126 @@
<!-- Item wrapper -->
<div
class="fuse-horizontal-navigation-item-wrapper"
[class.fuse-horizontal-navigation-item-has-subtitle]="!!item.subtitle"
[ngClass]="item.classes?.wrapper">
<!-- Item with an internal link -->
<ng-container *ngIf="item.link && !item.externalLink && !item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an external link -->
<ng-container *ngIf="item.link && item.externalLink && !item.function && !item.disabled">
<a
class="fuse-horizontal-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a function -->
<ng-container *ngIf="!item.link && item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an internal link and function -->
<ng-container *ngIf="item.link && !item.externalLink && item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an external link and function -->
<ng-container *ngIf="item.link && item.externalLink && item.function && !item.disabled">
<a
class="fuse-horizontal-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a no link and no function -->
<ng-container *ngIf="!item.link && !item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item is disabled -->
<ng-container *ngIf="item.disabled">
<div class="fuse-horizontal-navigation-item fuse-horizontal-navigation-item-disabled">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
</div>
<!-- Item template -->
<ng-template #itemTemplate>
<!-- Icon -->
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-horizontal-navigation-item-title-wrapper">
<div class="fuse-horizontal-navigation-item-title">
<span [ngClass]="item.classes?.title">
{{item.title}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-horizontal-navigation-item-subtitle text-hint">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<ng-container *ngIf="item.badge">
<div class="fuse-horizontal-navigation-item-badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</ng-container>
</ng-template>

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