Compare commits

...

196 Commits

Author SHA1 Message Date
Sercan Yemen
5faab90a7b Merge branch 'dev' into dev-starter 2023-01-19 11:22:48 +03:00
Sercan Yemen
1e8c284c93 Added release date to the changelog 2023-01-19 11:20:22 +03:00
Sercan Yemen
7efe9d5950 Fixed: Tooltip background color 2023-01-19 11:10:04 +03:00
Sercan Yemen
e380f754d7 Updated Angular & Angular Material 2023-01-19 11:09:20 +03:00
Sercan Yemen
104a1ceffb Fixed the routing 2022-12-12 14:58:29 +03:00
Sercan Yemen
ce0cce00d9 Merge branch 'dev' into dev-starter 2022-12-12 14:51:27 +03:00
Sercan Yemen
e39ec222d0 Updated Angular & Angular Material versions
Updated various other packages
Updated changelog and the version number
2022-12-12 14:50:08 +03:00
Sercan Yemen
685e981438 (@fuse/overrides/angular-material) Fixed: Tailwind breaks the disabled mat-checkbox styling 2022-12-07 19:38:51 +03:00
Sercan Yemen
ba8689ed0c (core) Use the new "CanMatch" rather than "CanLoad" and "CanActivate" for routes 2022-12-02 14:17:05 +03:00
Sercan Yemen
41a1692e01 (@fuse) Fixed: Autocompleted input background color is not correct 2022-11-25 15:06:42 +03:00
Sercan Yemen
d4736e2c02 (@fuse) Removed the unnecessary map wrap 2022-11-25 11:49:21 +03:00
Sercan Yemen
41bd49d45d Merge branch 'dev' into dev-starter 2022-11-21 09:07:40 +03:00
Sercan Yemen
1968227e19 Updated the version number and the changelog 2022-11-21 09:06:42 +03:00
Sercan Yemen
042979269e Merge branch 'dev' into dev-starter 2022-11-21 09:03:30 +03:00
Sercan Yemen
28180c7491 (@fuse/confirmation-dialog) Fixed: Dialog padding is broken because of MDC component class names 2022-11-20 14:11:00 +03:00
Sercan Yemen
de7a598c07 Updated the meta tags for the Demo 2022-11-18 11:14:00 +03:00
Sercan Yemen
8ae3cdfbea Updated the changelog 2022-11-18 11:07:04 +03:00
Sercan Yemen
b294672452 Merge branch 'dev' into dev-starter 2022-11-18 09:40:19 +03:00
Sercan Yemen
533e39261b (Dependencies) Removed ngx-markdown package, you can keep using it if you need, we will remove anything that slow us down in terms of updates 2022-11-18 09:38:54 +03:00
Sercan Yemen
db6fcc208b Merge branch 'dev' into dev-starter 2022-11-17 13:17:47 +03:00
Sercan Yemen
7870e312b9 (Dependencies) Updated various other dependencies 2022-11-17 13:11:55 +03:00
Sercan Yemen
1662042e35 (Dependencies) Updated the Angular & Angular Material to v15 stable 2022-11-17 13:10:53 +03:00
Sercan Yemen
08266c3e51 Updated the application to reflect the changes 2022-11-11 15:30:12 +03:00
Sercan Yemen
8000e53bae (overrides/angular-material) Tweaks and improvements 2022-11-11 15:29:56 +03:00
Sercan Yemen
8fbef029c0 Updated the Fuse version number and the changelog 2022-11-11 13:25:34 +03:00
Sercan Yemen
b7b849ee60 Second step of upgrading to Angular Material v15 (MDC). Almost re-wrote the entire override styles to make sure MDC components will look correct. 2022-11-11 13:21:04 +03:00
Sercan Yemen
872dffe42b Updated Angular to v15-rc.3 and Angular Material to v15-rc.2 2022-11-10 14:16:05 +03:00
Sercan Yemen
646b084a12 Only write the user themes file if the map has been changed. This fixes the issue where watcher being triple triggered on cold starts. 2022-11-08 12:47:38 +03:00
Sercan Yemen
c88d30a4f4 Removed the linter from the default project since linting mostly done using Editors and IDEs 2022-11-07 11:04:27 +03:00
Sercan Yemen
f2d5bf3041 Matched the files and folders to be the same with a newly generated Angular v15 project 2022-11-07 11:01:23 +03:00
Sercan Yemen
388456b937 First step for upgrading to Angular & Angular Material v15 2022-11-07 10:07:27 +03:00
Sercan Yemen
36403c8ebb Merge branch 'dev' into dev-starter 2022-10-04 10:31:18 +03:00
Sercan Yemen
127280c040 Updated version number, packages and the changelog 2022-10-04 10:27:41 +03:00
Sercan Yemen
cad7cd43f0 (ui/forms/fields) Added examples of number fields 2022-10-03 11:12:56 +03:00
Sercan Yemen
41756200ca (apps/mailbox) Fixed: Back button on details view is not working 2022-10-02 21:13:30 +03:00
Sercan Yemen
4a5f641c1c (layout/common/quick-chat) Fixed: Firefox doesn't like having sticky positioned elements inside a fixed positioned parent. This fix implements a mutation observer for '.cdk-global-scrollblock'. 2022-09-06 11:49:16 +03:00
Sercan Yemen
39ad558455 (FuseVerticalNavigation) Fixed: Firefox doesn't like having sticky positioned elements inside a fixed positioned parent. This fix implements a mutation observer for '.cdk-global-scrollblock'. 2022-09-06 11:49:05 +03:00
Sercan Yemen
404c4f5c07 Replace Moment with Luxon, closes #183
Big thanks to @steveschmitt for the majority of the work.
2022-08-24 13:15:38 +03:00
Sercan Yemen
51ee16ed93 (app.resolvers) Removed the extra user getter call since the user data already comes with the sign-in process to prevent confusion for the newcomers 2022-08-24 11:06:58 +03:00
Steve Schmitt
607f2afa90 Merge branch 'demo' into luxon 2022-08-22 08:11:29 -07:00
Sercan Yemen
baf35619bd Merge branch 'dev' into dev-starter 2022-08-22 11:19:48 +03:00
Sercan Yemen
2a8586ce45 Merge branch 'dev' into dev-starter 2022-08-22 11:18:29 +03:00
Sercan Yemen
6599587e31 (dependencies) Updated Angular & Angular Material & various other packages
Updated the version number and changelog
2022-08-22 11:06:45 +03:00
Sercan Yemen
3fa878b3f2 (apps/mailbox) Better label toggle behavior
(apps/tasks) Better tag toggle behavior
2022-08-22 10:53:19 +03:00
Sercan Yemen
a24bc3fc95 (tailwind) Removed the "aspect-ratio" plugin in favor of included "aspect" utility 2022-08-10 13:08:52 +03:00
Sercan Yemen
9b1c0f2541 (dashboards/project) Removed unnecessary classes from the buttons 2022-08-05 13:36:00 +03:00
Sercan Yemen
3ba64abf6e (tailwind.config) Fixed: typo on comment 2022-07-26 10:50:14 +03:00
Sercan Yemen
82ed80e35d (@fuse/tailwind/plugins/theming) Simplified the plugin and added more detailed comments 2022-07-25 15:17:55 +03:00
Sercan Yemen
72a63b30ca (docs) Fixed: Typos 2022-07-24 12:49:29 +03:00
Sercan Yemen
d2b37efed4 (layout/common/messages) Fixed: Wrong color classes on "No Messages" icon
(layout/common/notifications) Fixed: Wrong color classes on "No Notifications" icon
(layout/common/shortcuts) Fixed: Wrong color classes on "No Shortcuts" icon
2022-07-24 11:29:02 +03:00
Sercan Yemen
d5ec5a6fcd Merge branch 'dev' into dev-starter 2022-07-21 11:56:35 +03:00
Sercan Yemen
6d516a119a (dependencies) Updated Angular & Angular Material to v14.1.0
(dependencies) Updated various other packages
2022-07-21 11:51:28 +03:00
Steve Schmitt
3adf9bee6e Replace moment with luxon 2022-07-13 18:37:31 -07:00
Sercan Yemen
bb06bccf91 (@fuse/overrides/angular-material) Fixed: mat-hint position is not working 2022-06-28 08:34:48 +03:00
Sercan Yemen
3018c0235f Added the missing release date to the changelog 2022-06-03 11:00:40 +03:00
Sercan Yemen
8e513ccf1e Merge branch 'dev' into dev-starter 2022-06-03 10:52:35 +03:00
Sercan Yemen
a6399c0cf4 (@fuse) Fixed couple lint issues 2022-06-03 10:46:40 +03:00
Sercan Yemen
22d91673ae (dependencies) Updated to Angular and Angular Material v14.0.0 stable
(dependencies) Updated various other packages
2022-06-03 10:41:07 +03:00
Sercan Yemen
3e303a041a Updated the changelog 2022-06-02 11:02:30 +03:00
Sercan Yemen
714bc49c4e Updated the version number and added the changelog 2022-06-02 10:48:17 +03:00
Sercan Yemen
3cc4f31238 (dependencies) Updated Angular & Angular Material 2022-06-02 10:28:07 +03:00
Sercan Yemen
c5aa094230 (icons) Removed iconsmind icons pack because of the bundle size concerns 2022-05-27 13:48:31 +03:00
Sercan Yemen
cbd35d57f8 (global) Replaced iconsmind icons with heroicons 2022-05-27 13:45:52 +03:00
Sercan Yemen
11d5dcdb9d Updated various other packages 2022-05-26 14:01:50 +03:00
Sercan Yemen
66e5511402 Migrated to Angular Material to 14.rc-1 2022-05-26 13:59:01 +03:00
Sercan Yemen
20a03c3689 Migrated to Angular to 14.rc-2 2022-05-26 13:57:37 +03:00
Sercan Yemen
d033470851 (layout) Fixed: The theme settings cog is not positioned correctly in Empty layout 2022-05-25 23:17:12 +03:00
Sercan Yemen
c374c2c6cf (global) Replaced linked buttons with a tags + removed unnecessary routerLink attributes from some buttons 2022-05-25 13:29:47 +03:00
Sercan Yemen
30d75c9bd7 (@fuse/navigation) Keep the fragment as null if it's not provided to prevent empty hashtag appearing at the end of the url 2022-05-25 12:28:25 +03:00
Sercan Yemen
54cd21f496 (docs) Updated docs for FuseNavigation 2022-05-25 12:18:24 +03:00
Sercan Yemen
57d87fa1c4 (@fuse/navigation) Added "fragment" and "queryParams" support along with "preserveFragment" and "queryParamsHandling" 2022-05-25 12:17:56 +03:00
Sercan Yemen
cd8c6ece0f (@fuse/service/confirmation) Cleaned-up the dialog component 2022-05-25 11:43:23 +03:00
Sercan Yemen
1bf4c48cdc (@fuse/service/platform) Added comments to the service file 2022-05-25 11:25:37 +03:00
Sercan Yemen
ea9efc3dc2 (global) Added custom scrollbar styling for platforms other than macOS and iOS 2022-05-24 14:39:02 +03:00
Sercan Yemen
878a6bf191 (@fuse/services/platform) Added platform checker service 2022-05-24 14:38:50 +03:00
Sercan Yemen
099e745a36 (apps/academy) Fixed: Ring outside the current step's number is not visible 2022-05-24 12:58:06 +03:00
Sercan Yemen
150ddc64d7 (mock-api) Replaced the 'refresh-access-token' url with 'sign-in-with-token'
(auth) Changed the url on 'signInUsingToken' method to reflect the mock-api changes
2022-05-24 12:43:51 +03:00
Sercan Yemen
a6d64b1747 (auth) Made the renewing token on "sign-in-with-token" process an optional step to simplify the login process 2022-05-24 12:42:23 +03:00
Sercan Yemen
6eff4a1898 (@fuse/overrides/quill) Added tooltip styles for better compatibility, closes #171 2022-05-24 12:17:45 +03:00
Sercan Yemen
e3630218b5 (apps/mailbox) Fixed: Quill editor tooltips are not positioning correctly 2022-05-24 12:17:09 +03:00
Sercan Yemen
154095da0f (auth/sign-up) Added missing ngForm reference, closes #165 2022-05-24 11:30:10 +03:00
Sercan Yemen
a32970b7c2 (@fuse/navigation) Moved the margin into the children's first child for smoother animations, closes #173 2022-05-24 09:56:44 +03:00
Sercan Yemen
2771d3b1ae (global) Replace @import with @use 2022-05-17 12:40:52 +03:00
Sercan Yemen
9b44793b64 (ui/page-layouts) Fixed: Toggle button selected background is not visible 2022-05-15 15:28:28 +03:00
Sercan Yemen
9521257b4d (apps/mailbox) Fixed: Empty route related issues 2022-05-12 23:50:43 +03:00
Sercan Yemen
5378a6b6ef (apps/chat) Fixed: Empty route related issues 2022-05-12 23:49:42 +03:00
Sercan Yemen
c41e48df7f Angular 14 related changes 2022-05-12 23:40:40 +03:00
Sercan Yemen
b87173b056 (layout/common/search) Implemented the new MatAutocomplete "autoSelectActiveOption" functionality 2022-05-12 23:37:33 +03:00
Sercan Yemen
6aaa355a48 (ui/icons) Updated the route configuration 2022-05-12 23:28:07 +03:00
Sercan Yemen
b96dd041d9 (docs) Updated the directory structure 2022-05-12 23:25:51 +03:00
Sercan Yemen
9786c6baf5 (mock-api/file-manager) Fixed: http params are not working properly because of the new string conversion 2022-05-12 23:18:10 +03:00
Sercan Yemen
f5cc14939c (apps/chat) Fixed: Empty route related issues 2022-05-12 23:13:02 +03:00
Sercan Yemen
8fd434600b Updated various other packages 2022-05-12 21:36:32 +03:00
Sercan Yemen
4c82f6749b Updated Angular & Angular Material to 14.0.0-rc.0 2022-05-12 21:23:12 +03:00
Sercan Yemen
b0830148a3 (@fuse) Better scrolling for Quill editor 2022-05-12 13:36:40 +03:00
Sercan Yemen
cdc54ab05e (Documentation) Updated the FuseDrawer documentation and example 2022-03-29 16:38:49 +03:00
Sercan Yemen
e6ad547d27 (FuseDrawer) Updated the overlay and animation handling code for better stability 2022-03-29 16:38:42 +03:00
Sercan Yemen
4430754020 Merge remote-tracking branch 'origin/demo' into starter 2022-02-21 12:08:29 +03:00
Sercan Yemen
9d93a2b060 Merge remote-tracking branch 'origin/demo' into starter 2022-02-21 12:00:18 +03:00
Sercan Yemen
afda4b35c9 Updated the changelog 2022-02-21 11:59:41 +03:00
Sercan Yemen
3b88638dee Updated various packages
Re-enabled AngularCLI cache since Tailwind related issues are appeared to be fixed
2022-02-21 11:47:35 +03:00
Sercan Yemen
b96182c848 (layouts/dense) Fixed: Navigation appearance doesn't set correctly on small devices 2022-01-14 11:36:38 +03:00
Sercan Yemen
443a103d6f (@fuse/masonry) Removed the unnecessary scss file 2022-01-14 11:35:41 +03:00
Sercan Yemen
42241b279d (@fuse/navigation) Expose the width variables as css custom properties, so they can be overridden from the outside
(@fuse/drawer) Expose the width variable as a css custom property, so it can be overridden from the outside
2022-01-14 11:35:12 +03:00
Sercan Yemen
e5eaea5013 Fixed: Typo on changelog 2022-01-01 23:05:09 +03:00
Sercan Yemen
6e85b9ff2a Fixed: Typo on changelog 2022-01-01 23:01:51 +03:00
Sercan Yemen
38a4c392fe Merge remote-tracking branch 'origin/demo' into starter 2022-01-01 22:52:20 +03:00
Sercan Yemen
6e593b1f1e Updated version number and the changelog 2022-01-01 22:50:22 +03:00
Sercan Yemen
57a3f6ae99 Updated various packages 2022-01-01 22:47:49 +03:00
Sercan Yemen
700a117654 Moved to the new syntax on Tailwind config 2022-01-01 22:33:23 +03:00
Sercan Yemen
5337c95782 Fixed: Typo on resolve object 2022-01-01 22:32:39 +03:00
Sercan Yemen
964c4b23a9 Fixed: Accent and Warn colors are not being generated for themes other than the Default 2022-01-01 22:32:24 +03:00
Sercan Yemen
d9dee128de Merge remote-tracking branch 'origin/demo' into starter 2021-12-22 14:58:21 +03:00
Sercan Yemen
a8337ce617 Removed extra import 2021-12-22 14:36:30 +03:00
Sercan Yemen
3ceda2cf3f Removed extract-config plugin entry 2021-12-22 14:28:13 +03:00
Sercan Yemen
805b50707e Updated Angular to v13.1
Updated ESLint to v13
Updated Tailwind to v3
Updated various other packages
2021-12-22 14:19:21 +03:00
sercan
3fd522de31 Updated changelog 2021-11-11 11:09:49 +03:00
sercan
b84fb68a47 Merge remote-tracking branch 'origin/demo' into starter 2021-11-11 11:01:19 +03:00
sercan
4a2c7560e8 Updated Angular to 13.0.1
Updated various packages
2021-11-11 10:55:59 +03:00
sercan
46753d5402 Updated various packages 2021-11-09 13:25:11 +03:00
sercan
9d3e971325 Updated the changelog 2021-11-05 15:36:34 +03:00
sercan
9d7c015fe7 (@fuse/navigation) Don't need to use interpolation on scss variables anymore since Tailwind doesn't cause any problems with them anymore 2021-11-05 14:55:53 +03:00
sercan
4b5cac11f2 (docs) Small typo fix on FuseNavigation docs 2021-11-05 14:24:53 +03:00
sercan
6c62d25c35 Added .npmrc file so we can use npm versions that are bigger than 6
Updated the .nvmrc file
2021-11-05 11:48:26 +03:00
sercan
5c46b1aaa4 Updated changelog 2021-11-05 11:37:05 +03:00
sercan
3a1a7d44b6 Updated RxJS to 7.4.0
Optimized import paths
2021-11-05 11:36:03 +03:00
sercan
0076b1ee8a Updated changelog 2021-11-05 11:00:42 +03:00
sercan
c69542679d Updated ngx-quill package 2021-11-05 10:52:33 +03:00
sercan
e4ebe2fd7e (Breaking Change) Removed Calendar app due to FullCalendar's Angular component not being properly developed in the new version
(Breaking Change) Removed Fuse DateRange component (alternative: https://github.com/fetrarij/ngx-daterangepicker-material)
2021-11-05 10:50:19 +03:00
sercan
f380d8ca16 Updated Angular to v13
Updated Angular Material to v13
Updated version numbers and added the changelog
Updated various packages
2021-11-04 13:20:44 +03:00
sercan
6b14c1db7f (@fuse/loading-bar) Separated the service and interceptor from the component, updated its docs 2021-11-04 12:25:24 +03:00
sercan
20901117d1 (layout/common) Small comment fix 2021-10-25 15:03:33 +03:00
sercan
83e3c85448 (@fuse/mock-api) Added 'head', 'jsonp' & 'options' support 2021-09-21 09:54:53 +03:00
sercan
3d6714008a Merge remote-tracking branch 'origin/demo' into starter 2021-09-10 12:27:59 +03:00
sercan
ea954a75de (app.routing) Fixed: Wrong and missing eslint exceptions 2021-09-10 12:26:21 +03:00
sercan
4d4b411f76 (AuthModule) Fixed: Wrong sign in page link 2021-09-10 12:25:23 +03:00
sercan
7b5217d009 (@fuse/loading-bar) Fixed: Don't turn off the auto mode on docs examples 2021-09-06 23:56:39 +03:00
sercan
e671100bc5 (@fuse/loading-bar) Fixed: Wrong method name on docs 2021-09-06 23:42:16 +03:00
sercan
f910615831 (@fuse/loading-bar) First iteration of the FuseLoadingBar component, service, interceptor and their documentation 2021-09-06 23:32:50 +03:00
sercan
778679e136 (dependencies) Updated Angular & Angular Material to 12.2.4 and updated various other packages 2021-09-06 23:32:03 +03:00
sercan
c25610ee4e (docs/other-components) Fixed: Weird mat-drawer issue 2021-09-06 22:52:41 +03:00
sercan
3ddd7e4533 (docs/fuse-components) Fixed: Weird mat-drawer issue 2021-09-06 22:52:09 +03:00
sercan
c21c362b84 (QuickChat) Added docs page 2021-09-06 21:18:50 +03:00
sercan
9f97d74911 (@fuse/overrides/angular-material) Fixed: Normal border color of the text field overrides the invalid and focus border colors on dark themes 2021-09-02 23:48:26 +03:00
sercan
4b37f2304a Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 16:44:22 +03:00
sercan
08aec7c3db Increased the version number 2021-08-31 16:43:38 +03:00
sercan
0f249f287e Increased the version number and updated the changelog 2021-08-31 16:43:18 +03:00
sercan
c7b8a4564b (QuickChat) Fixed: Wrong css causing a lot of problems 2021-08-31 16:42:57 +03:00
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
e1942f46fd Merge remote-tracking branch 'origin/demo' into starter 2021-08-12 16:57:58 +03:00
sercan
ce656430c8 Merge remote-tracking branch 'origin/demo' into starter 2021-07-29 10:40:12 +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
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
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
2189232273 Merge remote-tracking branch 'origin/demo' into starter 2021-07-02 23:11:01 +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
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
90891eb201 Merge remote-tracking branch 'origin/demo' into starter 2021-06-15 08:36:32 +03:00
sercan
6c7201b77a Merge remote-tracking branch 'origin/demo' into starter 2021-06-03 13:28:30 +03:00
sercan
90f869e7b9 Merge remote-tracking branch 'origin/demo' into starter 2021-05-31 10:46:26 +03:00
sercan
2d2db97416 Merge remote-tracking branch 'origin/demo' into starter 2021-05-24 14:33:12 +03:00
sercan
5daa2260e6 Merge remote-tracking branch 'origin/demo' into starter 2021-05-24 14:29:35 +03:00
sercan
af984fcba1 Merge remote-tracking branch 'origin/demo' into starter 2021-05-23 07:13:28 +03:00
sercan
97d3662417 Merge remote-tracking branch 'origin/demo' into starter 2021-05-21 12:17:06 +03:00
sercan
d897a244c8 Merge remote-tracking branch 'origin/demo' into starter 2021-05-18 16:25:53 +03:00
sercan
d146a92c79 Merge remote-tracking branch 'origin/demo' into starter 2021-05-15 13:18:13 +03:00
sercan
27b6858b76 Merge remote-tracking branch 'origin/demo' into starter 2021-05-06 17:12:50 +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
8dcf21cb1a Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 10:23:15 +03:00
sercan
d917f03883 Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 10:20:06 +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
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
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
959 changed files with 21706 additions and 121565 deletions

View File

@@ -1,17 +0,0 @@
# 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.

1
.gitignore vendored
View File

@@ -31,6 +31,7 @@ chrome-profiler-events*.json
.history/*
# misc
/.angular/cache
/.sass-cache
/connect.lock
/coverage

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
legacy-peer-deps=true

2
.nvmrc
View File

@@ -1 +1 @@
12
16

View File

@@ -4,7 +4,7 @@ This project was generated with [Angular CLI](https://github.com/angular/angular
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding

View File

@@ -20,7 +20,9 @@
"outputPath": "dist/fuse",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"allowedCommonJsDependencies": [
@@ -28,7 +30,9 @@
"highlight.js",
"crypto-js/enc-utf8",
"crypto-js/hmac-sha256",
"crypto-js/enc-base64"
"crypto-js/enc-base64",
"flat",
"quill"
],
"assets": [
"src/favicon-16x16.png",
@@ -69,12 +73,6 @@
"maximumError": "90kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
@@ -109,10 +107,11 @@
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon-16x16.png",
@@ -124,21 +123,8 @@
],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}
},
"defaultProject": "fuse",
"cli": {
"defaultCollection": "@angular-eslint/schematics"
}
}

View File

@@ -1,45 +0,0 @@
// 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
});
};

27147
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@fuse/demo",
"version": "13.5.0",
"name": "fuse-angular",
"version": "17.2.0",
"description": "Fuse - Angular Admin Template and Starter Project",
"author": "https://themeforest.net/user/srcn",
"license": "https://themeforest.net/licenses/standard",
@@ -10,82 +10,58 @@
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint"
"test": "ng test"
},
"dependencies": {
"@angular/animations": "12.2.1",
"@angular/cdk": "12.2.1",
"@angular/common": "12.2.1",
"@angular/compiler": "12.2.1",
"@angular/core": "12.2.1",
"@angular/forms": "12.2.1",
"@angular/material": "12.2.1",
"@angular/material-moment-adapter": "12.2.1",
"@angular/platform-browser": "12.2.1",
"@angular/platform-browser-dynamic": "12.2.1",
"@angular/router": "12.2.1",
"@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.27.3",
"@angular/animations": "15.1.1",
"@angular/cdk": "15.1.1",
"@angular/common": "15.1.1",
"@angular/compiler": "15.1.1",
"@angular/core": "15.1.1",
"@angular/forms": "15.1.1",
"@angular/material": "15.1.1",
"@angular/material-luxon-adapter": "15.1.1",
"@angular/platform-browser": "15.1.1",
"@angular/platform-browser-dynamic": "15.1.1",
"@angular/router": "15.1.1",
"@ngneat/transloco": "4.2.2",
"apexcharts": "3.36.3",
"crypto-js": "3.3.0",
"highlight.js": "11.2.0",
"highlight.js": "11.7.0",
"lodash-es": "4.17.21",
"moment": "2.29.1",
"ng-apexcharts": "1.5.12",
"ngx-markdown": "12.0.1",
"ngx-quill": "14.2.0",
"perfect-scrollbar": "1.5.2",
"luxon": "3.2.1",
"ng-apexcharts": "1.7.4",
"ngx-quill": "20.0.1",
"perfect-scrollbar": "1.5.5",
"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"
"rxjs": "7.8.0",
"tslib": "2.4.1",
"zone.js": "0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "12.2.1",
"@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.1",
"@angular/compiler-cli": "12.2.1",
"@tailwindcss/aspect-ratio": "0.2.1",
"@tailwindcss/line-clamp": "0.2.1",
"@tailwindcss/typography": "0.4.1",
"@types/chroma-js": "2.1.3",
"@angular-devkit/build-angular": "15.1.2",
"@angular/cli": "15.1.2",
"@angular/compiler-cli": "15.1.1",
"@tailwindcss/line-clamp": "0.4.2",
"@tailwindcss/typography": "0.5.9",
"@types/chroma-js": "2.1.4",
"@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.19",
"@typescript-eslint/eslint-plugin": "4.29.1",
"@typescript-eslint/parser": "4.29.1",
"autoprefixer": "10.3.1",
"chroma-js": "2.1.2",
"eslint": "7.32.0",
"eslint-plugin-import": "2.24.0",
"eslint-plugin-jsdoc": "36.0.7",
"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",
"@types/jasmine": "4.3.1",
"@types/lodash": "4.14.191",
"@types/lodash-es": "4.17.6",
"@types/luxon": "3.2.0",
"autoprefixer": "10.4.13",
"chroma-js": "2.4.2",
"jasmine-core": "4.5.0",
"karma": "6.4.1",
"karma-chrome-launcher": "3.1.1",
"karma-coverage": "2.2.0",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.0.0",
"lodash": "4.17.21",
"postcss": "8.3.6",
"tailwindcss": "2.2.7",
"typescript": "4.3.5"
"postcss": "8.4.21",
"tailwindcss": "3.2.4",
"typescript": "4.9.4"
}
}

View File

@@ -1,6 +1,5 @@
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 { filter, Subject, takeUntil } from 'rxjs';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { fuseAnimations } from '@fuse/animations';
import { FuseAlertAppearance, FuseAlertType } from '@fuse/components/alert/alert.types';
@@ -54,6 +53,7 @@ export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
*/
@HostBinding('class') get classList(): any
{
/* eslint-disable @typescript-eslint/naming-convention */
return {
'fuse-alert-appearance-border' : this.appearance === 'border',
'fuse-alert-appearance-fill' : this.appearance === 'fill',
@@ -71,6 +71,7 @@ export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
'fuse-alert-type-warning' : this.type === 'warning',
'fuse-alert-type-error' : this.type === 'error'
};
/* eslint-enable @typescript-eslint/naming-convention */
}
// -----------------------------------------------------------------------------------------------------
@@ -145,7 +146,7 @@ export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}

View File

@@ -38,12 +38,14 @@ export class FuseCardComponent implements OnChanges
*/
@HostBinding('class') get classList(): any
{
/* eslint-disable @typescript-eslint/naming-convention */
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
};
/* eslint-enable @typescript-eslint/naming-convention */
}
// -----------------------------------------------------------------------------------------------------

View File

@@ -1,101 +0,0 @@
<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>

View File

@@ -1,292 +0,0 @@
/* 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;
}
}
}
}

View File

@@ -1,682 +0,0 @@
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);
}
}

View File

@@ -1,32 +0,0 @@
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
{
}

View File

@@ -1 +0,0 @@
export * from '@fuse/components/date-range/public-api';

View File

@@ -1,2 +0,0 @@
export * from '@fuse/components/date-range/date-range.component';
export * from '@fuse/components/date-range/date-range.module';

View File

@@ -1,14 +1,16 @@
/* Variables */
$fuse-drawer-width: 320;
:root {
--fuse-drawer-width: 320px;
}
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;
width: var(--fuse-drawer-width);
min-width: var(--fuse-drawer-width);
max-width: var(--fuse-drawer-width);
z-index: 300;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .35);
@apply bg-card;
@@ -43,7 +45,7 @@ fuse-drawer {
/* Side mode */
&.fuse-drawer-mode-side {
margin-left: -#{$fuse-drawer-width}px;
margin-left: calc(var(--fuse-drawer-width) * -1);
&.fuse-drawer-opened {
margin-left: 0;
@@ -71,7 +73,7 @@ fuse-drawer {
/* Side mode */
&.fuse-drawer-mode-side {
margin-right: -#{$fuse-drawer-width}px;
margin-right: calc(var(--fuse-drawer-width) * -1);
&.fuse-drawer-opened {
margin-right: 0;
@@ -116,7 +118,7 @@ fuse-drawer {
left: 0;
right: 0;
z-index: 299;
opacity: 0;
opacity: 1;
background-color: rgba(0, 0, 0, 0.6);
/* Fixed mode */

View File

@@ -32,6 +32,7 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
@Output() readonly positionChanged: EventEmitter<FuseDrawerPosition> = new EventEmitter<FuseDrawerPosition>();
private _animationsEnabled: boolean = false;
private readonly _handleOverlayClick: any;
private _hovered: boolean = false;
private _overlay: HTMLElement;
private _player: AnimationPlayer;
@@ -47,6 +48,9 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
private _fuseUtilsService: FuseUtilsService
)
{
this._handleOverlayClick = (): void => {
this.close();
};
}
// -----------------------------------------------------------------------------------------------------
@@ -58,6 +62,7 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
*/
@HostBinding('class') get classList(): any
{
/* eslint-disable @typescript-eslint/naming-convention */
return {
'fuse-drawer-animations-enabled' : this._animationsEnabled,
'fuse-drawer-fixed' : this.fixed,
@@ -66,6 +71,7 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
'fuse-drawer-opened' : this.opened,
[`fuse-drawer-position-${this.position}`]: true
};
/* eslint-enable @typescript-eslint/naming-convention */
}
/**
@@ -211,6 +217,12 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
*/
ngOnDestroy(): void
{
// Finish the animation
if ( this._player )
{
this._player.finish();
}
// Deregister the drawer from the registry
this._fuseDrawerService.deregisterComponent(this.name);
}
@@ -312,12 +324,6 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
// 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');
@@ -336,25 +342,17 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
// 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
// Create 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();
});
// Play the animation
this._player.play();
// Add an event listener to the overlay
this._overlay.addEventListener('click', () => {
this.close();
});
this._overlay.addEventListener('click', this._handleOverlayClick);
}
/**
@@ -380,13 +378,13 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
// Once the animation is done...
this._player.onDone(() => {
// Destroy the player
this._player.destroy();
// If the backdrop still exists...
// If the overlay still exists...
if ( this._overlay )
{
// Remove the backdrop
// Remove the event listener
this._overlay.removeEventListener('click', this._handleOverlayClick);
// Remove the overlay
this._overlay.parentNode.removeChild(this._overlay);
this._overlay = null;
}

View File

@@ -9,7 +9,7 @@ import { CommonModule } from '@angular/common';
declarations: [
FuseFullscreenComponent
],
imports: [
imports : [
MatButtonModule,
MatIconModule,
MatTooltipModule,

View File

@@ -0,0 +1 @@
export * from '@fuse/components/loading-bar/public-api';

View File

@@ -0,0 +1,5 @@
<ng-container *ngIf="show">
<mat-progress-bar
[mode]="mode"
[value]="progress"></mat-progress-bar>
</ng-container>

View File

@@ -0,0 +1,7 @@
fuse-loading-bar {
position: fixed;
top: 0;
z-index: 999;
width: 100%;
height: 6px;
}

View File

@@ -0,0 +1,82 @@
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Subject, takeUntil } from 'rxjs';
import { FuseLoadingService } from '@fuse/services/loading';
@Component({
selector : 'fuse-loading-bar',
templateUrl : './loading-bar.component.html',
styleUrls : ['./loading-bar.component.scss'],
encapsulation: ViewEncapsulation.None,
exportAs : 'fuseLoadingBar'
})
export class FuseLoadingBarComponent implements OnChanges, OnInit, OnDestroy
{
@Input() autoMode: boolean = true;
mode: 'determinate' | 'indeterminate';
progress: number = 0;
show: boolean = false;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(private _fuseLoadingService: FuseLoadingService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Auto mode
if ( 'autoMode' in changes )
{
// Set the auto mode in the service
this._fuseLoadingService.setAutoMode(coerceBooleanProperty(changes.autoMode.currentValue));
}
}
/**
* On init
*/
ngOnInit(): void
{
// Subscribe to the service
this._fuseLoadingService.mode$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((value) => {
this.mode = value;
});
this._fuseLoadingService.progress$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((value) => {
this.progress = value;
});
this._fuseLoadingService.show$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((value) => {
this.show = value;
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}
}

View File

@@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar/loading-bar.component';
@NgModule({
declarations: [
FuseLoadingBarComponent
],
imports : [
CommonModule,
MatProgressBarModule
],
exports : [
FuseLoadingBarComponent
]
})
export class FuseLoadingBarModule
{
}

View File

@@ -0,0 +1,2 @@
export * from '@fuse/components/loading-bar/loading-bar.component';
export * from '@fuse/components/loading-bar/loading-bar.module';

View File

@@ -5,7 +5,6 @@ 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'

View File

@@ -10,6 +10,10 @@
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[fragment]="item.fragment ?? null"
[preserveFragment]="item.preserveFragment ?? false"
[queryParams]="item.queryParams ?? null"
[queryParamsHandling]="item.queryParamsHandling ?? null"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''">
@@ -45,6 +49,10 @@
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[fragment]="item.fragment ?? null"
[preserveFragment]="item.preserveFragment ?? false"
[queryParams]="item.queryParams ?? null"
[queryParamsHandling]="item.queryParamsHandling ?? null"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''"

View File

@@ -1,7 +1,6 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { IsActiveMatchOptions } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject, takeUntil } from 'rxjs';
import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -76,7 +75,7 @@ export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDes
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}
}

View File

@@ -1,8 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { BooleanInput } from '@angular/cdk/coercion';
import { MatMenu } from '@angular/material/menu';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject, takeUntil } from 'rxjs';
import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -64,7 +63,7 @@ export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDe
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}

View File

@@ -1,6 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject, takeUntil } from 'rxjs';
import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -56,7 +55,7 @@ export class FuseHorizontalNavigationDividerItemComponent implements OnInit, OnD
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}
}

View File

@@ -1,6 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { Subject, takeUntil } from 'rxjs';
import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -56,7 +55,7 @@ export class FuseHorizontalNavigationSpacerItemComponent implements OnInit, OnDe
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}
}

View File

@@ -76,7 +76,7 @@ export class FuseHorizontalNavigationComponent implements OnChanges, OnInit, OnD
this._fuseNavigationService.deregisterComponent(this.name);
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}

View File

@@ -1,4 +1,4 @@
import { IsActiveMatchOptions } from '@angular/router';
import { IsActiveMatchOptions, Params, QueryParamsHandling } from '@angular/router';
export interface FuseNavigationItem
{
@@ -17,6 +17,10 @@ export interface FuseNavigationItem
disabled?: boolean;
tooltip?: string;
link?: string;
fragment?: string;
preserveFragment?: boolean;
queryParams?: Params | null;
queryParamsHandling?: QueryParamsHandling | null;
externalLink?: boolean;
target?:
| '_blank'

View File

@@ -1,8 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { BooleanInput } from '@angular/cdk/coercion';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { filter, Subject, takeUntil } from 'rxjs';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -98,7 +97,7 @@ export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnIn
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}

View File

@@ -10,6 +10,10 @@
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[fragment]="item.fragment ?? null"
[preserveFragment]="item.preserveFragment ?? false"
[queryParams]="item.queryParams ?? null"
[queryParamsHandling]="item.queryParamsHandling ?? null"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''">
@@ -45,6 +49,10 @@
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[fragment]="item.fragment ?? null"
[preserveFragment]="item.preserveFragment ?? false"
[queryParams]="item.queryParams ?? null"
[queryParamsHandling]="item.queryParamsHandling ?? null"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''"

View File

@@ -1,7 +1,6 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { IsActiveMatchOptions } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject, takeUntil } from 'rxjs';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -76,7 +75,7 @@ export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestr
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}
}

View File

@@ -1,8 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { BooleanInput } from '@angular/cdk/coercion';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { filter, Subject, takeUntil } from 'rxjs';
import { fuseAnimations } from '@fuse/animations';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
@@ -49,10 +48,12 @@ export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, O
*/
@HostBinding('class') get classList(): any
{
/* eslint-disable @typescript-eslint/naming-convention */
return {
'fuse-vertical-navigation-item-collapsed': this.isCollapsed,
'fuse-vertical-navigation-item-expanded' : this.isExpanded
};
/* eslint-enable @typescript-eslint/naming-convention */
}
// -----------------------------------------------------------------------------------------------------
@@ -176,7 +177,7 @@ export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, O
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}

View File

@@ -1,6 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject, takeUntil } from 'rxjs';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -56,7 +55,7 @@ export class FuseVerticalNavigationDividerItemComponent implements OnInit, OnDes
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}
}

View File

@@ -1,7 +1,6 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { BooleanInput } from '@angular/cdk/coercion';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject, takeUntil } from 'rxjs';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -62,7 +61,7 @@ export class FuseVerticalNavigationGroupItemComponent implements OnInit, OnDestr
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}

View File

@@ -1,6 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { Subject, takeUntil } from 'rxjs';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -56,7 +55,7 @@ export class FuseVerticalNavigationSpacerItemComponent implements OnInit, OnDest
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}
}

View File

@@ -1,20 +1,22 @@
/* Variables */
$fuse-vertical-navigation-compact-width: 112;
:root {
--fuse-vertical-navigation-compact-width: 112px;
}
fuse-vertical-navigation {
/* Compact appearance overrides */
&.fuse-vertical-navigation-appearance-compact {
width: #{$fuse-vertical-navigation-compact-width}px;
min-width: #{$fuse-vertical-navigation-compact-width}px;
max-width: #{$fuse-vertical-navigation-compact-width}px;
width: var(--fuse-vertical-navigation-compact-width);
min-width: var(--fuse-vertical-navigation-compact-width);
max-width: var(--fuse-vertical-navigation-compact-width);
/* Left positioned */
&.fuse-vertical-navigation-position-left {
/* Side mode */
&.fuse-vertical-navigation-mode-side {
margin-left: -#{$fuse-vertical-navigation-compact-width}px;
margin-left: calc(var(--fuse-vertical-navigation-compact-width) * -1);
}
/* Opened */
@@ -28,7 +30,7 @@ fuse-vertical-navigation {
/* Side mode */
&.fuse-vertical-navigation-mode-side {
margin-right: -#{$fuse-vertical-navigation-compact-width}px;
margin-right: calc(var(--fuse-vertical-navigation-compact-width) * -1);
}
/* Opened */
@@ -39,7 +41,7 @@ fuse-vertical-navigation {
/* Aside wrapper */
.fuse-vertical-navigation-aside-wrapper {
left: auto;
right: #{$fuse-vertical-navigation-compact-width}px;
right: var(--fuse-vertical-navigation-compact-width);
}
}
@@ -104,7 +106,7 @@ fuse-vertical-navigation {
/* Aside wrapper */
.fuse-vertical-navigation-aside-wrapper {
left: #{$fuse-vertical-navigation-compact-width}px;
left: var(--fuse-vertical-navigation-compact-width);
}
}
}

View File

@@ -1,5 +1,7 @@
/* Variables */
$fuse-vertical-navigation-width: 280;
:root {
--fuse-vertical-navigation-width: 280px;
}
fuse-vertical-navigation {
position: sticky;
@@ -7,9 +9,9 @@ fuse-vertical-navigation {
flex-direction: column;
flex: 1 0 auto;
top: 0;
width: #{$fuse-vertical-navigation-width}px;
min-width: #{$fuse-vertical-navigation-width}px;
max-width: #{$fuse-vertical-navigation-width}px;
width: var(--fuse-vertical-navigation-width);
min-width: var(--fuse-vertical-navigation-width);
max-width: var(--fuse-vertical-navigation-width);
height: 100vh;
min-height: 100vh;
max-height: 100vh;
@@ -45,7 +47,7 @@ fuse-vertical-navigation {
/* Side mode */
&.fuse-vertical-navigation-mode-side {
margin-left: -#{$fuse-vertical-navigation-width}px;
margin-left: calc(#{var(--fuse-vertical-navigation-width)} * -1);
&.fuse-vertical-navigation-opened {
margin-left: 0;
@@ -73,7 +75,7 @@ fuse-vertical-navigation {
/* Side mode */
&.fuse-vertical-navigation-mode-side {
margin-right: -#{$fuse-vertical-navigation-width}px;
margin-right: calc(var(--fuse-vertical-navigation-width) * -1);
&.fuse-vertical-navigation-opened {
margin-right: 0;
@@ -170,8 +172,8 @@ fuse-vertical-navigation {
flex-direction: column;
top: 0;
bottom: 0;
left: #{$fuse-vertical-navigation-width}px;
width: #{$fuse-vertical-navigation-width}px;
left: var(--fuse-vertical-navigation-width);
width: var(--fuse-vertical-navigation-width);
height: 100%;
z-index: 5;
overflow-x: hidden;
@@ -196,7 +198,7 @@ fuse-vertical-navigation {
.fuse-vertical-navigation-aside-wrapper {
left: auto;
right: #{$fuse-vertical-navigation-width}px;
right: var(--fuse-vertical-navigation-width);
}
}
@@ -335,7 +337,10 @@ fuse-vertical-navigation {
}
> .fuse-vertical-navigation-item-children {
margin-top: 6px;
> *:first-child {
margin-top: 6px;
}
> *:last-child {
padding-bottom: 6px;

View File

@@ -1,6 +1,8 @@
/* Variables */
$fuse-vertical-navigation-width: 280;
$fuse-vertical-navigation-dense-width: 80;
:root {
--fuse-vertical-navigation-width: 280px;
--fuse-vertical-navigation-dense-width: 80px;
}
fuse-vertical-navigation {
@@ -8,16 +10,16 @@ fuse-vertical-navigation {
&.fuse-vertical-navigation-appearance-dense {
&:not(.fuse-vertical-navigation-mode-over) {
width: #{$fuse-vertical-navigation-dense-width}px;
min-width: #{$fuse-vertical-navigation-dense-width}px;
max-width: #{$fuse-vertical-navigation-dense-width}px;
width: var(--fuse-vertical-navigation-dense-width);
min-width: var(--fuse-vertical-navigation-dense-width);
max-width: var(--fuse-vertical-navigation-dense-width);
/* Left positioned */
&.fuse-vertical-navigation-position-left {
/* Side mode */
&.fuse-vertical-navigation-mode-side {
margin-left: -#{$fuse-vertical-navigation-dense-width}px;
margin-left: calc(var(--fuse-vertical-navigation-dense-width) * -1);
}
/* Opened */
@@ -31,7 +33,7 @@ fuse-vertical-navigation {
/* Side mode */
&.fuse-vertical-navigation-mode-side {
margin-right: -#{$fuse-vertical-navigation-dense-width}px;
margin-right: calc(var(--fuse-vertical-navigation-dense-width) * -1);
}
/* Opened */
@@ -42,14 +44,14 @@ fuse-vertical-navigation {
/* Aside wrapper */
.fuse-vertical-navigation-aside-wrapper {
left: auto;
right: #{$fuse-vertical-navigation-dense-width}px;
right: var(--fuse-vertical-navigation-dense-width);
}
&.fuse-vertical-navigation-hover {
.fuse-vertical-navigation-aside-wrapper {
left: auto;
right: #{$fuse-vertical-navigation-width}px;
right: var(--fuse-vertical-navigation-width);
}
}
}
@@ -69,9 +71,9 @@ fuse-vertical-navigation {
.fuse-vertical-navigation-item-wrapper {
.fuse-vertical-navigation-item {
width: #{$fuse-vertical-navigation-width}px - 24px;
min-width: #{$fuse-vertical-navigation-width}px - 24px;
max-width: #{$fuse-vertical-navigation-width}px - 24px;
width: calc(var(--fuse-vertical-navigation-dense-width) - 24px);
min-width: calc(var(--fuse-vertical-navigation-dense-width) - 24px);
max-width: calc(var(--fuse-vertical-navigation-dense-width) - 24px);
.fuse-vertical-navigation-item-arrow,
.fuse-vertical-navigation-item-badge,
@@ -142,20 +144,23 @@ fuse-vertical-navigation {
/* Aside wrapper */
.fuse-vertical-navigation-aside-wrapper {
left: #{$fuse-vertical-navigation-dense-width}px;
left: var(--fuse-vertical-navigation-dense-width);
}
/* Hover */
&.fuse-vertical-navigation-hover {
.fuse-vertical-navigation-wrapper {
width: #{$fuse-vertical-navigation-width}px;
width: var(--fuse-vertical-navigation-width);
.fuse-vertical-navigation-content {
.fuse-vertical-navigation-item-wrapper {
.fuse-vertical-navigation-item {
width: calc(var(--fuse-vertical-navigation-width) - 24px);
min-width: calc(var(--fuse-vertical-navigation-width) - 24px);
max-width: calc(var(--fuse-vertical-navigation-width) - 24px);
.fuse-vertical-navigation-item-arrow,
.fuse-vertical-navigation-item-badge,
@@ -170,7 +175,7 @@ fuse-vertical-navigation {
}
.fuse-vertical-navigation-aside-wrapper {
left: #{$fuse-vertical-navigation-width}px;
left: var(--fuse-vertical-navigation-width);
}
}
}

View File

@@ -1,19 +1,21 @@
/* Variables */
$fuse-vertical-navigation-thin-width: 80;
:root {
--fuse-vertical-navigation-thin-width: 80px;
}
fuse-vertical-navigation {
/* Thin appearance overrides */
&.fuse-vertical-navigation-appearance-thin {
width: #{$fuse-vertical-navigation-thin-width}px;
min-width: #{$fuse-vertical-navigation-thin-width}px;
max-width: #{$fuse-vertical-navigation-thin-width}px;
width: var(--fuse-vertical-navigation-thin-width);
min-width: var(--fuse-vertical-navigation-thin-width);
max-width: var(--fuse-vertical-navigation-thin-width);
/* Left positioned */
&.fuse-vertical-navigation-position-left {
&.fuse-vertical-navigation-mode-side {
margin-left: -#{$fuse-vertical-navigation-thin-width}px;
margin-left: calc(var(--fuse-vertical-navigation-thin-width) * -1);
}
&.fuse-vertical-navigation-opened {
@@ -25,7 +27,7 @@ fuse-vertical-navigation {
&.fuse-vertical-navigation-position-right {
&.fuse-vertical-navigation-mode-side {
margin-right: -#{$fuse-vertical-navigation-thin-width}px;
margin-right: calc(var(--fuse-vertical-navigation-thin-width) * -1);
}
&.fuse-vertical-navigation-opened {
@@ -34,7 +36,7 @@ fuse-vertical-navigation {
.fuse-vertical-navigation-aside-wrapper {
left: auto;
right: #{$fuse-vertical-navigation-thin-width}px;
right: var(--fuse-vertical-navigation-thin-width);
}
}
@@ -91,7 +93,7 @@ fuse-vertical-navigation {
/* Aside wrapper */
.fuse-vertical-navigation-aside-wrapper {
left: #{$fuse-vertical-navigation-thin-width}px;
left: var(--fuse-vertical-navigation-thin-width);
}
}
}

View File

@@ -1,4 +1,4 @@
@import 'styles/appearances/default';
@import 'styles/appearances/compact';
@import 'styles/appearances/dense';
@import 'styles/appearances/thin';
@use 'styles/appearances/default';
@use 'styles/appearances/compact';
@use 'styles/appearances/dense';
@use 'styles/appearances/thin';

View File

@@ -1,9 +1,9 @@
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, QueryList, Renderer2, SimpleChanges, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, HostListener, Inject, Input, OnChanges, OnDestroy, OnInit, Output, QueryList, Renderer2, SimpleChanges, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
import { DOCUMENT } from '@angular/common';
import { NavigationEnd, Router } from '@angular/router';
import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';
import { merge, ReplaySubject, Subject, Subscription } from 'rxjs';
import { delay, filter, takeUntil } from 'rxjs/operators';
import { delay, filter, merge, ReplaySubject, Subject, Subscription, takeUntil } from 'rxjs';
import { fuseAnimations } from '@fuse/animations';
import { FuseNavigationItem, FuseVerticalNavigationAppearance, FuseVerticalNavigationMode, FuseVerticalNavigationPosition } from '@fuse/components/navigation/navigation.types';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
@@ -52,6 +52,7 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
private readonly _handleAsideOverlayClick: any;
private readonly _handleOverlayClick: any;
private _hovered: boolean = false;
private _mutationObserver: MutationObserver;
private _overlay: HTMLElement;
private _player: AnimationPlayer;
private _scrollStrategy: ScrollStrategy = this._scrollStrategyOptions.block();
@@ -65,6 +66,7 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
constructor(
private _animationBuilder: AnimationBuilder,
private _changeDetectorRef: ChangeDetectorRef,
@Inject(DOCUMENT) private _document: Document,
private _elementRef: ElementRef,
private _renderer2: Renderer2,
private _router: Router,
@@ -90,6 +92,7 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
*/
@HostBinding('class') get classList(): any
{
/* eslint-disable @typescript-eslint/naming-convention */
return {
'fuse-vertical-navigation-animations-enabled' : this._animationsEnabled,
[`fuse-vertical-navigation-appearance-${this.appearance}`]: true,
@@ -101,6 +104,7 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
'fuse-vertical-navigation-position-left' : this.position === 'left',
'fuse-vertical-navigation-position-right' : this.position === 'right'
};
/* eslint-enable @typescript-eslint/naming-convention */
}
/**
@@ -329,6 +333,34 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
*/
ngAfterViewInit(): void
{
// Fix for Firefox.
//
// Because 'position: sticky' doesn't work correctly inside a 'position: fixed' parent,
// adding the '.cdk-global-scrollblock' to the html element breaks the navigation's position.
// This fixes the problem by reading the 'top' value from the html element and adding it as a
// 'marginTop' to the navigation itself.
this._mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
const mutationTarget = mutation.target as HTMLElement;
if ( mutation.attributeName === 'class' )
{
if ( mutationTarget.classList.contains('cdk-global-scrollblock') )
{
const top = parseInt(mutationTarget.style.top, 10);
this._renderer2.setStyle(this._elementRef.nativeElement, 'margin-top', `${Math.abs(top)}px`);
}
else
{
this._renderer2.setStyle(this._elementRef.nativeElement, 'margin-top', null);
}
}
});
});
this._mutationObserver.observe(this._document.documentElement, {
attributes : true,
attributeFilter: ['class']
});
setTimeout(() => {
// Return if 'navigation content' element does not exist
@@ -374,6 +406,9 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
*/
ngOnDestroy(): void
{
// Disconnect the mutation observer
this._mutationObserver.disconnect();
// Forcefully close the navigation and aside in case they are opened
this.close();
this.closeAside();
@@ -382,7 +417,7 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
this._fuseNavigationService.deregisterComponent(this.name);
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}

View File

@@ -1,7 +1,6 @@
import { Directive, ElementRef, OnDestroy, OnInit } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { filter, Subject, takeUntil } from 'rxjs';
@Directive({
selector: '[fuseScrollReset]',
@@ -47,7 +46,7 @@ export class FuseScrollResetDirective implements OnInit, OnDestroy
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}
}

View File

@@ -2,8 +2,7 @@ import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChang
import { Router } from '@angular/router';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { Platform } from '@angular/cdk/platform';
import { fromEvent, Subject } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { debounceTime, fromEvent, Subject, takeUntil } from 'rxjs';
import PerfectScrollbar from 'perfect-scrollbar';
import { merge } from 'lodash-es';
import { ScrollbarGeometry, ScrollbarPosition } from '@fuse/directives/scrollbar/scrollbar.types';
@@ -138,7 +137,7 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
this._destroy();
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.next(null);
this._unsubscribeAll.complete();
}

View File

@@ -2,17 +2,19 @@ import { NgModule, Optional, SkipSelf } from '@angular/core';
import { MATERIAL_SANITY_CHECKS } from '@angular/material/core';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
import { FuseConfirmationModule } from '@fuse/services/confirmation';
import { FuseLoadingModule } from '@fuse/services/loading';
import { FuseMediaWatcherModule } from '@fuse/services/media-watcher/media-watcher.module';
import { FusePlatformModule } from '@fuse/services/platform/platform.module';
import { FuseSplashScreenModule } from '@fuse/services/splash-screen/splash-screen.module';
import { FuseTailwindConfigModule } from '@fuse/services/tailwind/tailwind.module';
import { FuseUtilsModule } from '@fuse/services/utils/utils.module';
@NgModule({
imports : [
FuseConfirmationModule,
FuseLoadingModule,
FuseMediaWatcherModule,
FusePlatformModule,
FuseSplashScreenModule,
FuseTailwindConfigModule,
FuseUtilsModule
],
providers: [

View File

@@ -1,7 +1,6 @@
import { Inject, Injectable } from '@angular/core';
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { delay, switchMap } from 'rxjs/operators';
import { delay, Observable, of, switchMap, throwError } from 'rxjs';
import { FUSE_MOCK_API_DEFAULT_DELAY } from '@fuse/lib/mock-api/mock-api.constants';
import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service';

View File

@@ -1,6 +1,5 @@
import { HttpRequest } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { take } from 'rxjs/operators';
import { Observable, of, take, throwError } from 'rxjs';
import { FuseMockApiReplyCallback } from '@fuse/lib/mock-api/mock-api.types';
export class FuseMockApiHandler

View File

@@ -9,11 +9,14 @@ import { FuseMockApiMethods } from '@fuse/lib/mock-api/mock-api.types';
export class FuseMockApiService
{
private _handlers: { [key: string]: Map<string, FuseMockApiHandler> } = {
'delete': new Map<string, FuseMockApiHandler>(),
'get' : new Map<string, FuseMockApiHandler>(),
'patch' : new Map<string, FuseMockApiHandler>(),
'post' : new Map<string, FuseMockApiHandler>(),
'put' : new Map<string, FuseMockApiHandler>()
'get' : new Map<string, FuseMockApiHandler>(),
'post' : new Map<string, FuseMockApiHandler>(),
'patch' : new Map<string, FuseMockApiHandler>(),
'delete' : new Map<string, FuseMockApiHandler>(),
'put' : new Map<string, FuseMockApiHandler>(),
'head' : new Map<string, FuseMockApiHandler>(),
'jsonp' : new Map<string, FuseMockApiHandler>(),
'options': new Map<string, FuseMockApiHandler>()
};
/**
@@ -86,18 +89,7 @@ export class FuseMockApiService
}
/**
* Register a DELETE request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
*/
onDelete(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('delete', url, delay);
}
/**
* Register a GET request handler
* Register GET request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
@@ -108,18 +100,7 @@ export class FuseMockApiService
}
/**
* Register a PATCH request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
*/
onPatch(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('patch', url, delay);
}
/**
* Register a POST request handler
* Register POST request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
@@ -130,7 +111,29 @@ export class FuseMockApiService
}
/**
* Register a PUT request handler
* Register PATCH request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
*/
onPatch(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('patch', url, delay);
}
/**
* Register DELETE request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
*/
onDelete(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('delete', url, delay);
}
/**
* Register PUT request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
@@ -140,6 +143,39 @@ export class FuseMockApiService
return this._registerHandler('put', url, delay);
}
/**
* Register HEAD request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
*/
onHead(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('head', url, delay);
}
/**
* Register JSONP request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
*/
onJsonp(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('jsonp', url, delay);
}
/**
* Register OPTIONS request handler
*
* @param url - URL address of the mocked API endpoint
* @param delay - Delay of the response in milliseconds
*/
onOptions(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('options', url, delay);
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------

View File

@@ -8,6 +8,9 @@ export type FuseMockApiReplyCallback =
export type FuseMockApiMethods =
| 'get'
| 'post'
| 'put'
| 'patch'
| 'delete';
| 'delete'
| 'put'
| 'head'
| 'jsonp'
| 'options';

View File

@@ -35,6 +35,7 @@ export class FuseConfigService
this._config.next(config);
}
// eslint-disable-next-line @typescript-eslint/member-ordering
get config$(): Observable<any>
{
return this._config.asObservable();

View File

@@ -10,7 +10,7 @@ import { CommonModule } from '@angular/common';
declarations: [
FuseConfirmationDialogComponent
],
imports: [
imports : [
MatButtonModule,
MatDialogModule,
MatIconModule,

View File

@@ -1,52 +1,36 @@
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Component, Inject, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { FuseConfirmationConfig } from '@fuse/services/confirmation/confirmation.types';
@Component({
selector : 'fuse-confirmation-dialog',
templateUrl : './dialog.component.html',
styles : [
/* language=SCSS */
`
.fuse-confirmation-dialog-panel {
@screen md {
@apply w-128;
}
.mat-dialog-container {
padding: 0 !important;
.mat-mdc-dialog-container {
.mat-mdc-dialog-surface {
padding: 0 !important;
}
}
}
`
],
encapsulation: ViewEncapsulation.None
})
export class FuseConfirmationDialogComponent implements OnInit
export class FuseConfirmationDialogComponent
{
/**
* Constructor
*/
constructor(
@Inject(MAT_DIALOG_DATA) public data: FuseConfirmationConfig,
public matDialogRef: MatDialogRef<FuseConfirmationDialogComponent>
)
constructor(@Inject(MAT_DIALOG_DATA) public data: FuseConfirmationConfig)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
}

View File

@@ -0,0 +1 @@
export * from '@fuse/services/loading/public-api';

View File

@@ -0,0 +1,48 @@
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { finalize, Observable } from 'rxjs';
import { FuseLoadingService } from '@fuse/services/loading/loading.service';
@Injectable()
export class FuseLoadingInterceptor implements HttpInterceptor
{
handleRequestsAutomatically: boolean;
/**
* Constructor
*/
constructor(
private _fuseLoadingService: FuseLoadingService
)
{
// Subscribe to the auto
this._fuseLoadingService.auto$
.subscribe((value) => {
this.handleRequestsAutomatically = value;
});
}
/**
* Intercept
*
* @param req
* @param next
*/
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
{
// If the Auto mode is turned off, do nothing
if ( !this.handleRequestsAutomatically )
{
return next.handle(req);
}
// Set the loading status to true
this._fuseLoadingService._setLoadingStatus(true, req.url);
return next.handle(req).pipe(
finalize(() => {
// Set the status to false if there are any errors or the request is completed
this._fuseLoadingService._setLoadingStatus(false, req.url);
}));
}
}

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { FuseLoadingInterceptor } from '@fuse/services/loading/loading.interceptor';
@NgModule({
providers: [
{
provide : HTTP_INTERCEPTORS,
useClass: FuseLoadingInterceptor,
multi : true
}
]
})
export class FuseLoadingModule
{
}

View File

@@ -0,0 +1,146 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class FuseLoadingService
{
private _auto$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
private _mode$: BehaviorSubject<'determinate' | 'indeterminate'> = new BehaviorSubject<'determinate' | 'indeterminate'>('indeterminate');
private _progress$: BehaviorSubject<number | null> = new BehaviorSubject<number | null>(0);
private _show$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
private _urlMap: Map<string, boolean> = new Map<string, boolean>();
/**
* Constructor
*/
constructor(private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for auto mode
*/
get auto$(): Observable<boolean>
{
return this._auto$.asObservable();
}
/**
* Getter for mode
*/
get mode$(): Observable<'determinate' | 'indeterminate'>
{
return this._mode$.asObservable();
}
/**
* Getter for progress
*/
get progress$(): Observable<number>
{
return this._progress$.asObservable();
}
/**
* Getter for show
*/
get show$(): Observable<boolean>
{
return this._show$.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Show the loading bar
*/
show(): void
{
this._show$.next(true);
}
/**
* Hide the loading bar
*/
hide(): void
{
this._show$.next(false);
}
/**
* Set the auto mode
*
* @param value
*/
setAutoMode(value: boolean): void
{
this._auto$.next(value);
}
/**
* Set the mode
*
* @param value
*/
setMode(value: 'determinate' | 'indeterminate'): void
{
this._mode$.next(value);
}
/**
* Set the progress of the bar manually
*
* @param value
*/
setProgress(value: number): void
{
if ( value < 0 || value > 100 )
{
console.error('Progress value must be between 0 and 100!');
return;
}
this._progress$.next(value);
}
/**
* Sets the loading status on the given url
*
* @param status
* @param url
*/
_setLoadingStatus(status: boolean, url: string): void
{
// Return if the url was not provided
if ( !url )
{
console.error('The request URL must be provided!');
return;
}
if ( status === true )
{
this._urlMap.set(url, status);
this._show$.next(true);
}
else if ( status === false && this._urlMap.has(url) )
{
this._urlMap.delete(url);
}
// Only set the status to 'false' if all outgoing requests are completed
if ( this._urlMap.size === 0 )
{
this._show$.next(false);
}
}
}

View File

@@ -0,0 +1,2 @@
export * from '@fuse/services/loading/loading.service';
export * from '@fuse/services/loading/loading.module';

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
import { Observable, ReplaySubject } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { FuseTailwindService } from '@fuse/services/tailwind/tailwind.service';
import { map, Observable, ReplaySubject, switchMap } from 'rxjs';
import { fromPairs } from 'lodash-es';
import { FuseConfigService } from '@fuse/services/config';
@Injectable()
export class FuseMediaWatcherService
@@ -14,11 +14,12 @@ export class FuseMediaWatcherService
*/
constructor(
private _breakpointObserver: BreakpointObserver,
private _fuseTailwindConfigService: FuseTailwindService
private _fuseConfigService: FuseConfigService
)
{
this._fuseTailwindConfigService.tailwindConfig$.pipe(
switchMap(config => this._breakpointObserver.observe(Object.values(config.breakpoints)).pipe(
this._fuseConfigService.config$.pipe(
map(config => fromPairs(Object.entries(config.screens).map(([alias, screen]) => ([alias, `(min-width: ${screen})`])))),
switchMap(screens => this._breakpointObserver.observe(Object.values(screens)).pipe(
map((state) => {
// Prepare the observable values and set their defaults
@@ -30,7 +31,7 @@ export class FuseMediaWatcherService
for ( const [query] of matchingBreakpoints )
{
// Find the alias of the matching query
const matchingAlias = Object.entries(config.breakpoints).find(([alias, q]) => q === query)[0];
const matchingAlias = Object.entries(screens).find(([alias, q]) => q === query)[0];
// Add the matching query to the observable values
if ( matchingAlias )

View File

@@ -0,0 +1 @@
export * from '@fuse/services/platform/public-api';

View File

@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { FusePlatformService } from '@fuse/services/platform/platform.service';
@NgModule({
providers: [
FusePlatformService
]
})
export class FusePlatformModule
{
/**
* Constructor
*/
constructor(private _fusePlatformService: FusePlatformService)
{
}
}

View File

@@ -0,0 +1,59 @@
import { Injectable } from '@angular/core';
import { Platform } from '@angular/cdk/platform';
@Injectable({
providedIn: 'root'
})
export class FusePlatformService
{
osName = 'os-unknown';
/**
* Constructor
*/
constructor(private _platform: Platform)
{
// If the platform is not a browser, return immediately
if ( !this._platform.isBrowser )
{
return;
}
// Windows
if ( navigator.userAgent.includes('Win') )
{
this.osName = 'os-windows';
}
// Mac OS
if ( navigator.userAgent.includes('Mac') )
{
this.osName = 'os-mac';
}
// Unix
if ( navigator.userAgent.includes('X11') )
{
this.osName = 'os-unix';
}
// Linux
if ( navigator.userAgent.includes('Linux') )
{
this.osName = 'os-linux';
}
// iOS
if ( this._platform.IOS )
{
this.osName = 'os-ios';
}
// Android
if ( this._platform.ANDROID )
{
this.osName = 'os-android';
}
}
}

View File

@@ -0,0 +1 @@
export * from '@fuse/services/platform/platform.service';

View File

@@ -1,7 +1,7 @@
import { Inject, Injectable } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { NavigationEnd, Router } from '@angular/router';
import { filter, take } from 'rxjs/operators';
import { filter, take } from 'rxjs';
@Injectable()
export class FuseSplashScreenService

View File

@@ -1 +0,0 @@
export * from '@fuse/services/tailwind/public-api';

View File

@@ -1,2 +0,0 @@
export * from '@fuse/services/tailwind/tailwind.module';
export * from '@fuse/services/tailwind/tailwind.service';

View File

@@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { FuseTailwindService } from '@fuse/services/tailwind/tailwind.service';
@NgModule({
providers: [
FuseTailwindService
]
})
export class FuseTailwindConfigModule
{
/**
* Constructor
*/
constructor(private _fuseTailwindConfigService: FuseTailwindService)
{
}
}

View File

@@ -1,58 +0,0 @@
import { Injectable } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs';
import { fromPairs, map } from 'lodash-es';
import * as extractedTailwindConfigStyle from '@fuse/styles/core/tailwind-config.scss';
@Injectable()
export class FuseTailwindService
{
private _tailwindConfig: ReplaySubject<any> = new ReplaySubject<any>(1);
/**
* Constructor
*/
constructor()
{
// Prepare the config object
const config: any = {};
// Extract the style from the class
const regexpForClass = /\.fuse-tailwind-extracted-config\s\{([\s\S]*)\}/g;
const style = regexpForClass.exec(extractedTailwindConfigStyle.default)[1].trim();
// Extract the rules
const regexp = /(--[\s\S]*?):'([\s\S]*?)';/g;
let rules = regexp.exec(style);
// Add to the config
while ( rules !== null )
{
const configGroup = /--([\s\S]*?)-/g.exec(rules[1])[1];
if ( !config[configGroup] )
{
config[configGroup] = {};
}
config[configGroup][rules[1].replace(/(--[\s\S]*?-)/g, '')] = rules[2];
rules = regexp.exec(style);
}
// Parse the themes objects
config.themes = fromPairs(map(config.themes, (value, key) => [key, JSON.parse(value)]));
// Execute the observable with the config
this._tailwindConfig.next(config);
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for _tailwindConfig
*/
get tailwindConfig$(): Observable<any>
{
return this._tailwindConfig.asObservable();
}
}

View File

@@ -20,22 +20,22 @@ textarea {
}
&:-webkit-autofill {
-webkit-transition: 'background-color 9999s ease-out';
-webkit-transition-delay: 9999s;
transition: background-color 600000s 0s, color 600000s 0s !important;
}
&:-webkit-autofill:hover {
-webkit-transition: 'background-color 9999s ease-out';
-webkit-transition-delay: 9999s;
transition: background-color 600000s 0s, color 600000s 0s !important;
}
&:-webkit-autofill:focus {
-webkit-transition: 'background-color 9999s ease-out';
-webkit-transition-delay: 9999s;
transition: background-color 600000s 0s, color 600000s 0s !important;
}
&:-webkit-autofill:active {
-webkit-transition: 'background-color 9999s ease-out';
-webkit-transition-delay: 9999s;
transition: background-color 600000s 0s, color 600000s 0s !important;
}
[data-autocompleted] {
background-color: transparent !important;
}
}

View File

@@ -1,6 +0,0 @@
/* ----------------------------------------------------------------------------------------------------- */
/* @ Any configuration we need from Tailwind's config file will be extracted here so we can import this
/* @ file from "config.ts" to access the extracted configuration from TypeScript
/* ----------------------------------------------------------------------------------------------------- */
@variants fuse-tailwind-extracted-config {
}

View File

@@ -1,13 +1,9 @@
/* 1. Core */
@import 'core/tailwind-config';
/* 1. Components */
@use 'components/example-viewer';
@use 'components/input';
/* 2. Components */
@import 'components/example-viewer';
@import 'components/input';
/* 3. Overrides */
@import 'overrides/angular-material';
@import 'overrides/fullcalendar';
@import 'overrides/highlightjs';
@import 'overrides/perfect-scrollbar';
@import 'overrides/quill';
/* 2. Overrides */
@use 'overrides/angular-material';
@use 'overrides/highlightjs';
@use 'overrides/perfect-scrollbar';
@use 'overrides/quill';

File diff suppressed because it is too large Load Diff

View File

@@ -1,680 +0,0 @@
/* ----------------------------------------------------------------------------------------------------- */
/* @ FullCalendar overrides
/* ----------------------------------------------------------------------------------------------------- */
.fc {
.fc-view-container {
/* Day Grid - Month view */
.fc-view.fc-dayGridMonth-view {
.fc-head {
> tr > .fc-head-container {
border: none;
.fc-row {
.fc-day-header {
border-color: var(--fuse-divider);
span {
display: flex;
align-items: center;
justify-content: center;
padding-top: 8px;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
@apply text-secondary;
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
border: none;
.fc-day-grid {
.fc-week {
.fc-bg {
.fc-day {
border-color: var(--fuse-divider);
&.fc-today {
background: none;
}
}
}
.fc-content-skeleton {
.fc-day-top {
text-align: center;
&.fc-other-month {
opacity: 1;
.fc-day-number {
@apply text-hint;
}
}
&.fc-today {
.fc-day-number {
@apply bg-primary text-on-primary;
}
}
.fc-day-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 21px;
margin: 4px 0;
font-size: 12px;
border-radius: 50%;
float: none;
}
}
.fc-event-container {
.fc-day-grid-event {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 6px 4px 6px;
padding: 0 5px;
font-size: 12px;
border-radius: 4px;
border: none;
cursor: pointer;
@screen sm {
padding: 0 8px;
}
}
}
.fc-more {
padding: 0 3px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
@apply text-secondary;
@screen sm {
padding: 0 6px;
}
}
}
.fc-highlight-skeleton {
.fc-highlight {
position: relative;
opacity: 1;
@apply bg-gray-100;
}
}
}
}
}
}
.fc-popover {
@apply bg-card;
&.fc-more-popover {
border: none;
border-radius: 4px;
@apply shadow-2xl;
.fc-header {
height: 32px;
min-height: 32px;
max-height: 32px;
padding: 0 8px;
@apply bg-hover;
.fc-title {
margin: 0;
padding: 0;
font-size: 12px;
}
}
.fc-body {
max-height: 160px;
overflow: hidden auto;
.fc-event-container {
padding: 8px;
.fc-day-grid-event {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 0 6px 0;
padding: 0 8px;
font-size: 12px;
line-height: 1;
border-radius: 4px;
border: none;
cursor: pointer;
&:last-child {
margin-bottom: 0;
}
}
}
}
}
}
}
/* Time Grid - Week view */
.fc-view.fc-timeGridWeek-view {
.fc-head {
> tr > .fc-head-container {
border: none;
.fc-row {
.fc-axis {
width: 48px !important;
border-color: var(--fuse-divider);
}
.fc-day-header {
border-color: var(--fuse-divider);
span {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
@apply text-secondary;
&.fc-weekday {
padding-top: 16px;
font-size: 12px;
letter-spacing: 0.055em;
text-transform: uppercase;
font-weight: 400;
}
&.fc-date {
padding-bottom: 12px;
font-size: 26px;
font-weight: 300;
}
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
border: none;
.fc-day-grid {
.fc-row {
min-height: 0;
.fc-bg {
.fc-axis {
width: 48px !important;
border-color: var(--fuse-divider);
}
.fc-day {
border-color: var(--fuse-divider);
&.fc-today {
background: none;
}
}
}
.fc-content-skeleton {
padding-bottom: 0;
.fc-axis {
width: 48px !important;
}
.fc-event-container {
.fc-day-grid-event {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 6px 6px 6px;
padding: 0 8px;
font-size: 12px;
line-height: 1;
border-radius: 4px;
border: none;
cursor: pointer;
}
}
}
}
}
.fc-divider {
border: none;
background: var(--fuse-divider);
}
.fc-time-grid {
.fc-bg {
.fc-axis {
border: none;
width: 48px !important;
+ .fc-day {
border: none;
}
}
.fc-day {
border-color: var(--fuse-divider);
&.fc-today {
background: none;
}
}
}
.fc-slats {
.fc-axis {
width: 48px !important;
height: 48px;
text-align: center;
span {
font-size: 12px;
width: 48px;
min-width: 48px;
}
}
.fc-time {
border-color: var(--fuse-divider);
}
.fc-widget-content {
border-color: var(--fuse-divider);
}
}
.fc-content-skeleton {
.fc-axis {
width: 48px !important;
}
.fc-event-container {
margin: 0 12px 0 0;
.fc-time-grid-event {
display: flex;
padding: 8px;
border-radius: 4px;
border: none;
cursor: pointer;
.fc-time,
.fc-title {
font-size: 12px;
}
}
}
}
}
}
}
}
/* Time Grid - Day view */
.fc-view.fc-timeGridDay-view {
.fc-head {
> tr > .fc-head-container {
border: none;
.fc-row {
.fc-axis {
width: 48px !important;
border-color: var(--fuse-divider);
}
.fc-day-header {
border-color: var(--fuse-divider);
span {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
@apply text-secondary;
&.fc-weekday {
padding-top: 16px;
font-size: 12px;
letter-spacing: 0.055em;
text-transform: uppercase;
font-weight: 400;
}
&.fc-date {
padding-bottom: 12px;
font-size: 26px;
font-weight: 300;
}
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
border: none;
.fc-day-grid {
.fc-bg {
.fc-axis {
border-color: var(--fuse-divider);
}
.fc-day {
border-color: var(--fuse-divider);
&.fc-today {
background: none;
}
}
}
.fc-row {
min-height: 0;
.fc-bg {
.fc-axis {
width: 48px !important;
}
}
.fc-content-skeleton {
padding-bottom: 0;
.fc-axis {
width: 48px !important;
}
.fc-event-container {
.fc-day-grid-event {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 6px 6px 6px;
padding: 0 8px;
font-size: 12px;
line-height: 1;
border-radius: 4px;
border: none;
cursor: pointer;
}
}
}
}
}
.fc-divider {
border: none;
border-color: var(--fuse-divider);
}
.fc-time-grid {
.fc-bg {
.fc-day {
border-color: var(--fuse-divider);
&.fc-today {
background: none;
}
}
.fc-axis {
border: none;
width: 48px !important;
+ .fc-day {
border: none;
}
}
}
.fc-slats {
.fc-axis {
width: 48px !important;
height: 48px;
text-align: center;
span {
font-size: 12px;
width: 48px;
min-width: 48px;
}
}
.fc-time {
border-color: var(--fuse-divider);
}
.fc-widget-content {
border-color: var(--fuse-divider);
}
}
.fc-content-skeleton {
.fc-axis {
width: 48px !important;
}
.fc-event-container {
margin: 0 12px 0 0;
.fc-time-grid-event {
display: flex;
padding: 8px;
border-radius: 4px;
border: none;
cursor: pointer;
.fc-time,
.fc-title {
font-size: 12px;
}
}
}
}
}
}
}
}
/* List - Year view */
.fc-view.fc-listYear-view {
border: none;
.fc-list-table {
.fc-list-heading {
display: none;
}
.fc-list-item {
display: flex;
cursor: pointer;
&:hover {
td {
@apply bg-hover;
}
}
td {
display: flex;
align-items: center;
width: auto;
height: 48px;
min-height: 48px;
padding: 0 8px;
border-width: 0 0 1px 0;
border-color: var(--fuse-divider);
&.fc-list-item-date {
order: 1;
padding-left: 16px;
width: 100px;
min-width: 100px;
max-width: 100px;
@screen sm {
width: 120px;
min-width: 120px;
max-width: 120px;
}
> span {
display: flex;
align-items: baseline;
span {
&:first-child {
display: flex;
justify-content: center;
padding-right: 2px;
width: 32px;
min-width: 32px;
max-width: 32px;
font-size: 18px;
@screen sm {
padding-right: 8px;
}
+ span {
display: flex;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.055em;
text-transform: uppercase;
@apply text-secondary;
}
}
}
}
}
&.fc-list-item-time {
flex: 0 0 auto;
order: 3;
width: 120px;
min-width: 120px;
max-width: 120px;
@screen sm {
width: 160px;
min-width: 160px;
max-width: 160px;
}
}
&.fc-list-item-marker {
flex: 0 0 auto;
order: 2;
.fc-event-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
}
&.fc-list-item-title {
flex: 1 1 auto;
order: 4;
padding-right: 24px;
font-weight: 500;
}
}
}
}
}
}
/* Day grid event - Dragging */
.fc-day-grid-event {
&.fc-dragging,
&.fc-resizing {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 6px 4px 6px;
padding: 0 8px;
font-size: 12px;
line-height: 1;
border-radius: 4px;
border: none;
}
}
}

View File

@@ -80,7 +80,9 @@
}
.ql-container {
overflow: hidden;
overflow: auto;
min-height: 160px;
max-height: 400px;
border-radius: 0 0 6px 6px;
@apply border-gray-300 border-opacity-100 shadow-sm #{'!important'};
@@ -89,17 +91,52 @@
}
.ql-editor {
min-height: 160px;
max-height: 160px;
height: 160px;
@apply bg-card;
.dark & {
background-color: rgba(0, 0, 0, 0.05);
//background-color: rgba(0, 0, 0, 0.05);
}
&.ql-blank::before {
@apply text-hint;
}
}
.ql-tooltip {
@apply px-3 py-1 shadow-sm rounded-md bg-gray-100 border-gray-300;
.dark & {
@apply shadow-lg bg-gray-700 border-gray-700 #{'!important'};
}
// Label
&:before {
@apply text-secondary;
}
.ql-action,
.ql-remove {
@apply text-primary border-gray-300;
.dark & {
@apply text-primary-400 border-gray-300;
}
}
.ql-action:after {
@apply border-r border-r-gray-300 #{'!important'};
.dark & {
@apply border-r-gray-500 #{'!important'};
}
}
input {
@apply rounded-sm text-default bg-white border-gray-300 #{'!important'};
.dark & {
@apply bg-gray-700 border-gray-500 #{'!important'};
}
}
}
}

View File

@@ -63,11 +63,15 @@
-webkit-text-fill-color: currentColor;
}
/* Set the background and foreground colors */
body, .dark, .light {
@apply text-default bg-default #{'!important'};
}
*, *::before, *::after {
/* Set the border color */
*,
::before,
::after {
--tw-border-opacity: 1 !important;
border-color: rgba(var(--fuse-border-rgb), var(--tw-border-opacity));
@@ -76,6 +80,47 @@
}
}
/* Style scrollbars on platforms other than MacOS and iOS */
@media only screen and (min-width: 960px) {
body:not(.os-mac) {
::-webkit-scrollbar {
width: 8px;
height: 8px;
background-color: rgba(0, 0, 0, 0);
}
::-webkit-scrollbar:hover {
width: 8px;
height: 8px;
background-color: rgba(0, 0, 0, 0.06);
}
::-webkit-scrollbar-thumb {
border: 2px solid transparent;
border-radius: 20px;
box-shadow: inset 0 0 0 20px rgba(0, 0, 0, 0.24);
}
::-webkit-scrollbar-thumb:active {
border-radius: 20px;
box-shadow: inset 0 0 0 20px rgba(0, 0, 0, 0.37);
}
&.dark {
::-webkit-scrollbar-thumb {
box-shadow: inset 0 0 0 20px rgba(255, 255, 255, 0.24);
}
::-webkit-scrollbar-thumb:active {
box-shadow: inset 0 0 0 20px rgba(255, 255, 255, 0.37);
}
}
}
}
/* Set the foreground color for disabled elements */
[disabled] * {
@apply text-disabled #{'!important'};
}

View File

@@ -1,167 +1,164 @@
@use '~@angular/material' as mat;
@use "sass:map";
@use '@angular/material' as mat;
@use "user-themes" as userThemes;
/* Set the base colors for light themes */
$light-base: (
foreground: (
base: #000000,
divider: #E2E8F0, /* slate.200 */
dividers: #E2E8F0, /* slate.200 */
disabled: #94A3B8, /* slate.400 */
disabled-button: #94A3B8, /* slate.400 */
disabled-text: #94A3B8, /* slate.400 */
elevation: #000000,
hint-text: #94A3B8, /* slate.400 */
secondary-text: #64748B, /* slate.500 */
icon: #64748B, /* slate.500 */
icons: #64748B, /* slate.500 */
mat-icon: #64748B, /* slate.500 */
text: #1E293B, /* slate.800 */
slider-min: #1E293B, /* slate.800 */
slider-off: #CBD5E1, /* slate.300 */
slider-off-active: #94A3B8 /* slate.400 */
),
background: (
status-bar: #CBD5E1, /* slate.300 */
app-bar: #FFFFFF,
background: #F1F5F9, /* slate.100 */
hover: rgba(148, 163, 184, 0.12), /* slate.400 + opacity */
card: #FFFFFF,
dialog: #FFFFFF,
disabled-button: rgba(148, 163, 184, 0.38), /* slate.400 + opacity */
raised-button: #FFFFFF,
focused-button: #64748B, /* slate.500 */
selected-button: #E2E8F0, /* slate.200 */
selected-disabled-button: #E2E8F0, /* slate.200 */
disabled-button-toggle: #CBD5E1, /* slate.300 */
unselected-chip: #E2E8F0, /* slate.200 */
disabled-list-option: #CBD5E1, /* slate.300 */
tooltip: #1E293B /* slate.800 */
)
);
/* Set the base colors for dark themes */
$dark-base: (
foreground: (
base: #FFFFFF,
divider: rgba(241, 245, 249, 0.12), /* slate.100 + opacity */
dividers: rgba(241, 245, 249, 0.12), /* slate.100 + opacity */
disabled: #475569, /* slate.600 */
disabled-button: #1E293B, /* slate.800 */
disabled-text: #475569, /* slate.600 */
elevation: #000000,
hint-text: #64748B, /* slate.500 */
secondary-text: #94A3B8, /* slate.400 */
icon: #F1F5F9, /* slate.100 */
icons: #F1F5F9, /* slate.100 */
mat-icon: #94A3B8, /* slate.400 */
text: #FFFFFF,
slider-min: #FFFFFF,
slider-off: #64748B, /* slate.500 */
slider-off-active: #94A3B8 /* slate.400 */
),
background: (
status-bar: #0F172A, /* slate.900 */
app-bar: #0F172A, /* slate.900 */
background: #0F172A, /* slate.900 */
hover: rgba(255, 255, 255, 0.05),
card: #1E293B, /* slate.800 */
dialog: #1E293B, /* slate.800 */
disabled-button: rgba(15, 23, 42, 0.38), /* slate.900 + opacity */
raised-button: #0F172A, /* slate.900 */
focused-button: #E2E8F0, /* slate.200 */
selected-button: rgba(255, 255, 255, 0.05),
selected-disabled-button: #1E293B, /* slate.800 */
disabled-button-toggle: #0F172A, /* slate.900 */
unselected-chip: #475569, /* slate.600 */
disabled-list-option: #E2E8F0, /* slate.200 */
tooltip: #64748B /* slate.500 */
)
);
/* Include the core Angular Material styles */
@include mat.core();
/* Create a base theme without color.
This will globally set the density and typography for all future color themes. */
/* Create a base theme without any color to set the density and typography */
@include mat.all-component-themes((
color: null,
density: -2,
density: 0,
typography: mat.define-typography-config(
$font-family: theme('fontFamily.sans'),
$title: mat.define-typography-level(1.25rem, 2rem, 600),
$body-2: mat.define-typography-level(0.875rem, 1.5rem, 600),
$button: mat.define-typography-level(0.875rem, 0.875rem, 500),
$input: mat.define-typography-level(0.875rem, 1.2857142857, 400) /* line-height: 20px */
$headline-1: mat.define-typography-level(1.875rem, 2.25rem, 800, theme('fontFamily.sans')),
$headline-2: mat.define-typography-level(1.25rem, 1.75rem, 700, theme('fontFamily.sans')),
$headline-3: mat.define-typography-level(1.125rem, 1.75rem, 600, theme('fontFamily.sans')),
$headline-4: mat.define-typography-level(0.875rem, 1.25rem, 600, theme('fontFamily.sans')),
$headline-5: mat.define-typography-level(0.875rem, 1.5rem, 400, theme('fontFamily.sans')),
$headline-6: mat.define-typography-level(0.875rem, 1.5rem, 400, theme('fontFamily.sans')),
$subtitle-1: mat.define-typography-level(1rem, 1.75rem, 400, theme('fontFamily.sans')),
$subtitle-2: mat.define-typography-level(0.875rem, 1.25rem, 600, theme('fontFamily.sans')),
$body-1: mat.define-typography-level(0.875rem, 1.5rem, 400, theme('fontFamily.sans')),
$body-2: mat.define-typography-level(0.875rem, 1.5rem, 400, theme('fontFamily.sans')),
$caption: mat.define-typography-level(0.75rem, 1rem, 400, theme('fontFamily.sans')),
$button: mat.define-typography-level(0.875rem, 0.875rem, 500, theme('fontFamily.sans')),
$overline: mat.define-typography-level(0.75rem, 2rem, 500, theme('fontFamily.sans'))
)
));
/* Generate Primary, Accent and Warn palettes */
$palettes: ();
@each $name in (primary, accent, warn) {
$palettes: map.merge($palettes, (#{$name}: (
50: var(--fuse-#{$name}-50),
100: var(--fuse-#{$name}-100),
200: var(--fuse-#{$name}-200),
300: var(--fuse-#{$name}-300),
400: var(--fuse-#{$name}-400),
500: var(--fuse-#{$name}-500),
600: var(--fuse-#{$name}-600),
700: var(--fuse-#{$name}-700),
800: var(--fuse-#{$name}-800),
900: var(--fuse-#{$name}-900),
contrast: (
50: var(--fuse-on-#{$name}-50),
100: var(--fuse-on-#{$name}-100),
200: var(--fuse-on-#{$name}-200),
300: var(--fuse-on-#{$name}-300),
400: var(--fuse-on-#{$name}-400),
500: var(--fuse-on-#{$name}-500),
600: var(--fuse-on-#{$name}-600),
700: var(--fuse-on-#{$name}-700),
800: var(--fuse-on-#{$name}-800),
900: var(--fuse-on-#{$name}-900)
),
default: var(--fuse-#{$name}),
lighter: var(--fuse-#{$name}-100),
darker: var(--fuse-#{$name}-700),
text: var(--fuse-#{$name}),
default-contrast: var(--fuse-on-#{$name}),
lighter-contrast: var(--fuse-on-#{$name}-100),
darker-contrast: var(--fuse-on-#{$name}-700)
)));
}
/* Loop through user themes and generate Angular Material themes */
@each $name, $theme in userThemes.$user-themes {
/* Generate Angular Material themes. Since we are using CSS Custom Properties,
we don't have to generate a separate Angular Material theme for each color
set. We can just create one light and one dark theme and then switch the
CSS Custom Properties to dynamically switch the colors. */
body.light,
body .light {
$base-light-theme: mat.define-light-theme((
color: ($palettes)
/* Generate the palettes */
$palettes: ();
@each $name in (primary, accent, warn) {
/* Define the Angular Material theme */
$palette: mat.define-palette(map.get($theme, $name));
/* Replace the default colors on the defined Material palette */
$palette: map.merge($palette, (
default: map.get(map.get($theme, $name), DEFAULT),
lighter: map.get(map.get($theme, $name), 100),
darker: map.get(map.get($theme, $name), 700),
text: map.get(map.get($theme, $name), DEFAULT),
default-contrast: map.get(map.get(map.get($theme, $name), contrast), DEFAULT),
lighter-contrast: map.get(map.get(map.get($theme, $name), contrast), 100),
darker-contrast: map.get(map.get(map.get($theme, $name), contrast), 700)
));
$palettes: map.merge($palettes, (#{$name}: $palette));
}
/* Define a light & dark Angular Material theme with the generated palettes */
$light-theme: mat.define-light-theme((
color: $palettes
));
$light-theme: (
color: (
primary: map.get(map.get($base-light-theme, color), primary),
accent: map.get(map.get($base-light-theme, color), accent),
warn: map.get(map.get($base-light-theme, color), warn),
is-dark: map.get(map.get($base-light-theme, color), is-dark),
foreground: (
base: #000000,
divider: #E2E8F0, /* blueGray.200 */
dividers: #E2E8F0, /* blueGray.200 */
disabled: #94A3B8, /* blueGray.400 */
disabled-button: #94A3B8, /* blueGray.400 */
disabled-text: #94A3B8, /* blueGray.400 */
elevation: #000000,
hint-text: #94A3B8, /* blueGray.400 */
secondary-text: #64748B, /* blueGray.500 */
icon: #64748B, /* blueGray.500 */
icons: #64748B, /* blueGray.500 */
mat-icon: #64748B, /* blueGray.500 */
text: #1E293B, /* blueGray.800 */
slider-min: #1E293B, /* blueGray.800 */
slider-off: #CBD5E1, /* blueGray.300 */
slider-off-active: #94A3B8 /* blueGray.400 */
),
background: (
status-bar: #CBD5E1, /* blueGray.300 */
app-bar: #FFFFFF,
background: #F1F5F9, /* blueGray.100 */
hover: rgba(148, 163, 184, 0.12), /* blueGray.400 + opacity */
card: #FFFFFF,
dialog: #FFFFFF,
disabled-button: rgba(148, 163, 184, 0.38), /* blueGray.400 + opacity */
raised-button: #FFFFFF,
focused-button: #64748B, /* blueGray.500 */
selected-button: #E2E8F0, /* blueGray.200 */
selected-disabled-button: #E2E8F0, /* blueGray.200 */
disabled-button-toggle: #CBD5E1, /* blueGray.300 */
unselected-chip: #E2E8F0, /* blueGray.200 */
disabled-list-option: #CBD5E1, /* blueGray.300 */
tooltip: #1E293B /* blueGray.800 */
)
)
);
/* Use all-component-colors to only generate the colors */
@include mat.all-component-colors($light-theme);
}
body.dark,
body .dark {
$base-dark-theme: mat.define-dark-theme((
color: ($palettes)
$dark-theme: mat.define-dark-theme((
color: $palettes
));
$dark-theme: (
color: (
primary: map.get(map.get($base-dark-theme, color), primary),
accent: map.get(map.get($base-dark-theme, color), accent),
warn: map.get(map.get($base-dark-theme, color), warn),
is-dark: map.get(map.get($base-dark-theme, color), is-dark),
foreground: (
base: #FFFFFF,
divider: rgba(241, 245, 249, 0.12), /* blueGray.100 + opacity */
dividers: rgba(241, 245, 249, 0.12), /* blueGray.100 + opacity */
disabled: #475569, /* blueGray.600 */
disabled-button: #1E293B, /* blueGray.800 */
disabled-text: #475569, /* blueGray.600 */
elevation: #000000,
hint-text: #64748B, /* blueGray.500 */
secondary-text: #94A3B8, /* blueGray.400 */
icon: #F1F5F9, /* blueGray.100 */
icons: #F1F5F9, /* blueGray.100 */
mat-icon: #94A3B8, /* blueGray.400 */
text: #FFFFFF,
slider-min: #FFFFFF,
slider-off: #64748B, /* blueGray.500 */
slider-off-active: #94A3B8 /* blueGray.400 */
),
background: (
status-bar: #0F172A, /* blueGray.900 */
app-bar: #0F172A, /* blueGray.900 */
background: #0F172A, /* blueGray.900 */
hover: rgba(255, 255, 255, 0.05),
card: #1E293B, /* blueGray.800 */
dialog: #1E293B, /* blueGray.800 */
disabled-button: rgba(15, 23, 42, 0.38), /* blueGray.900 + opacity */
raised-button: #0F172A, /* blueGray.900 */
focused-button: #E2E8F0, /* blueGray.200 */
selected-button: rgba(255, 255, 255, 0.05),
selected-disabled-button: #1E293B, /* blueGray.800 */
disabled-button-toggle: #0F172A, /* blueGray.900 */
unselected-chip: #475569, /* blueGray.600 */
disabled-list-option: #E2E8F0, /* blueGray.200 */
tooltip: #64748B /* blueGray.500 */
)
)
/* Merge the custom base colors with the generated themes */
$light-theme-colors: map.merge(map.get($light-theme, color), $light-base);
$light-theme: map.merge(
(color: $light-theme-colors),
$light-theme-colors
);
/* Use all-component-colors to only generate the colors */
@include mat.all-component-colors($dark-theme);
$dark-theme-colors: map.merge(map.get($dark-theme, color), $dark-base);
$dark-theme: map.merge(
(color: $dark-theme-colors),
$dark-theme-colors
);
/* Generate and encapsulate Angular Material themes */
#{map.get($theme, selector)} .light,
#{map.get($theme, selector)}.light {
@include mat.all-component-colors($light-theme);
}
#{map.get($theme, selector)} .dark,
#{map.get($theme, selector)}.dark {
@include mat.all-component-colors($dark-theme);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,38 +0,0 @@
const plugin = require('tailwindcss/plugin');
const buildMediaQuery = require('tailwindcss/lib/util/buildMediaQuery').default;
const extractConfig = plugin(({
addVariant,
theme
}) =>
{
addVariant('fuse-tailwind-extracted-config', ({container}) =>
{
// Prepare the extracted config variable
let extractedConfig = '';
// Breakpoints
Object.entries(theme('screens')).forEach(([key, value]) =>
{
extractedConfig = `${extractedConfig} --breakpoints-${key}:'${buildMediaQuery(value)}';`;
});
// Themes
(theme('fuse.themes')).forEach((value) =>
{
Object.entries(value).forEach(([key, value]) =>
{
extractedConfig = `${extractedConfig} --themes-${key}:'${JSON.stringify(value)}';`;
});
});
// Append the extracted config
container.append(`
.fuse-tailwind-extracted-config {
${extractedConfig}
}
`);
});
});
module.exports = extractConfig;

View File

@@ -1,17 +1,14 @@
const plugin = require('tailwindcss/plugin');
const iconSize = plugin(({
addUtilities,
theme,
e,
variants
module.exports = plugin(
({
matchUtilities,
theme
}) =>
{
const values = theme('iconSize');
addUtilities(
Object.entries(values).map(([key, value]) => ({
[`.${e(`icon-size-${key}`)}`]: {
matchUtilities(
{
'icon-size': (value) => ({
width : value,
height : value,
minWidth : value,
@@ -22,13 +19,14 @@ const iconSize = plugin(({
width : value,
height: value
}
}
})),
variants('iconSize')
);
})
},
{
values: theme('iconSize')
});
},
{
theme : {
theme: {
iconSize: {
3 : '0.75rem',
3.5: '0.875rem',
@@ -47,10 +45,6 @@ const iconSize = plugin(({
22 : '5.5rem',
24 : '6rem'
}
},
variants: {
iconSize: ['responsive']
}
});
module.exports = iconSize;
}
);

View File

@@ -1,17 +1,21 @@
const chroma = require('chroma-js');
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const colors = require('tailwindcss/colors');
const plugin = require('tailwindcss/plugin');
const flattenColorPalette = require('tailwindcss/lib/util/flattenColorPalette').default;
const generateContrasts = require(path.resolve(__dirname, ('../utils/generate-contrasts')));
const jsonToSassMap = require(path.resolve(__dirname, ('../utils/json-to-sass-map')));
// -----------------------------------------------------------------------------------------------------
// @ Utilities
// -----------------------------------------------------------------------------------------------------
/**
* Normalize the provided theme
* Normalizes the provided theme by omitting empty values and values that
* start with "on" from each palette. Also sets the correct DEFAULT value
* of each palette.
*
* @param theme
*/
@@ -28,67 +32,6 @@ const normalizeTheme = (theme) =>
));
};
/**
* Generates variable colors for the 'colors'
* configuration from the provided theme
*
* @param theme
*/
const generateVariableColors = (theme) =>
{
// https://github.com/adamwathan/tailwind-css-variable-text-opacity-demo
const customPropertiesWithOpacity = (name) => ({
opacityVariable,
opacityValue
}) =>
{
if ( opacityValue )
{
return `rgba(var(--fuse-${name}-rgb), ${opacityValue})`;
}
if ( opacityVariable )
{
return `rgba(var(--fuse-${name}-rgb), var(${opacityVariable}, 1))`;
}
return `rgb(var(--fuse-${name}-rgb))`;
};
return _.fromPairs(_.flatten(_.map(_.keys(flattenColorPalette(normalizeTheme(theme))), (name) => [
[name, customPropertiesWithOpacity(name)],
[`on-${name}`, customPropertiesWithOpacity(`on-${name}`)]
])));
};
/**
* Generate and return themes object with theme name and colors/
* This is useful for accessing themes from Angular (Typescript).
*
* @param themes
* @returns {unknown[]}
*/
function generateThemesObject(themes)
{
const normalizedDefaultTheme = normalizeTheme(themes.default);
return _.map(_.cloneDeep(themes), (value, key) =>
{
const theme = normalizeTheme(value);
const primary = (theme && theme.primary && theme.primary.DEFAULT) ? theme.primary.DEFAULT : normalizedDefaultTheme.primary.DEFAULT;
const accent = (theme && theme.accent && theme.accent.DEFAULT) ? theme.accent.DEFAULT : normalizedDefaultTheme.accent.DEFAULT;
const warn = (theme && theme.warn && theme.warn.DEFAULT) ? theme.warn.DEFAULT : normalizedDefaultTheme.warn.DEFAULT;
return _.fromPairs([
[
key,
{
primary,
accent,
warn
}
]
]);
});
}
// -----------------------------------------------------------------------------------------------------
// @ FUSE TailwindCSS Main Plugin
// -----------------------------------------------------------------------------------------------------
@@ -98,29 +41,113 @@ const theming = plugin.withOptions((options) => ({
theme
}) =>
{
// -----------------------------------------------------------------------------------------------------
// @ Map variable colors
// -----------------------------------------------------------------------------------------------------
const mapVariableColors = _.fromPairs(_.map(options.themes, (theme, themeName) => [
themeName === 'default' ? 'body' : `body.theme-${e(themeName)}`,
_.fromPairs(_.flatten(_.map(flattenColorPalette(_.fromPairs(_.flatten(_.map(normalizeTheme(theme), (palette, paletteName) => [
[
e(paletteName),
palette
],
[
`on-${e(paletteName)}`,
_.fromPairs(_.map(generateContrasts(palette), (color, hue) => [hue, _.get(theme, [`on-${paletteName}`, hue]) || color]))
]
])
))), (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]])))
/**
* Create user themes object by going through the provided themes and
* merging them with the provided "default" so, we can have a complete
* set of color palettes for each user theme.
*/
const userThemes = _.fromPairs(_.map(options.themes, (theme, themeName) => [
themeName,
_.defaults({}, theme, options.themes['default'])
]));
addComponents(mapVariableColors);
/**
* Normalize the themes and assign it to the themes object. This will
* be the final object that we create a SASS map from
*/
let themes = _.fromPairs(_.map(userThemes, (theme, themeName) => [
themeName,
normalizeTheme(theme)
]));
// -----------------------------------------------------------------------------------------------------
// @ Generate scheme based css custom properties and utility classes
// -----------------------------------------------------------------------------------------------------
/**
* Go through the themes to generate the contrasts and filter the
* palettes to only have "primary", "accent" and "warn" objects.
*/
themes = _.fromPairs(_.map(themes, (theme, themeName) => [
themeName,
_.pick(
_.fromPairs(_.map(theme, (palette, paletteName) => [
paletteName,
{
...palette,
contrast: _.fromPairs(_.map(generateContrasts(palette), (color, hue) => [
hue,
_.get(userThemes[themeName], [`on-${paletteName}`, hue]) || color
]))
}
])),
['primary', 'accent', 'warn']
)
]));
/**
* Go through the themes and attach appropriate class selectors so,
* we can use them to encapsulate each theme.
*/
themes = _.fromPairs(_.map(themes, (theme, themeName) => [
themeName,
{
selector: `".theme-${themeName}"`,
...theme
}
]));
/* Generate the SASS map using the themes object */
const sassMap = jsonToSassMap(JSON.stringify({'user-themes': themes}));
/* Get the file path */
const filename = path.resolve(__dirname, ('../../styles/user-themes.scss'));
/* Read the file and get its data */
let data;
try
{
data = fs.readFileSync(filename, {encoding: 'utf8'});
}
catch ( err )
{
console.error(err);
}
/* Write the file if the map has been changed */
if ( data !== sassMap )
{
try
{
fs.writeFileSync(filename, sassMap, {encoding: 'utf8'});
}
catch ( err )
{
console.error(err);
}
}
/**
* Iterate through the user's themes and build Tailwind components containing
* CSS Custom Properties using the colors from them. This allows switching
* themes by simply replacing a class name as well as nesting them.
*/
addComponents(
_.fromPairs(_.map(options.themes, (theme, themeName) => [
themeName === 'default' ? 'body, .theme-default' : `.theme-${e(themeName)}`,
_.fromPairs(_.flatten(_.map(flattenColorPalette(_.fromPairs(_.flatten(_.map(normalizeTheme(theme), (palette, paletteName) => [
[
e(paletteName),
palette
],
[
`on-${e(paletteName)}`,
_.fromPairs(_.map(generateContrasts(palette), (color, hue) => [hue, _.get(theme, [`on-${paletteName}`, hue]) || color]))
]
])
))), (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]])))
]))
);
/**
* Generate scheme based css custom properties and utility classes
*/
const schemeCustomProps = _.map(['light', 'dark'], (colorScheme) =>
{
const isDark = colorScheme === 'dark';
@@ -135,13 +162,13 @@ const theming = plugin.withOptions((options) => ({
/**
* If a custom property is not available, browsers will use
* the fallback value. In this case, we want to use '--is-dark'
* as the indicator of a dark theme so we can use it like this:
* as the indicator of a dark theme so, we can use it like this:
* background-color: var(--is-dark, red);
*
* If we set '--is-dark' as "true" on dark themes, the above rule
* won't work because of the said "fallback value" logic. Therefore,
* we set the '--is-dark' to "false" on light themes and not set it
* all on dark themes so that the fallback value can be used on
* at all on dark themes so that the fallback value can be used on
* dark themes.
*
* On light themes, since '--is-dark' exists, the above rule will be
@@ -156,7 +183,7 @@ const theming = plugin.withOptions((options) => ({
*/
...(!isDark ? {'--is-dark': 'false'} : {}),
// Generate custom properties from customProps
/* Generate custom properties from customProps */
..._.fromPairs(_.flatten(_.map(background, (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]]))),
..._.fromPairs(_.flatten(_.map(foreground, (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]])))
}
@@ -165,7 +192,7 @@ const theming = plugin.withOptions((options) => ({
const schemeUtilities = (() =>
{
// Generate general styles & utilities
/* Generate general styles & utilities */
return {};
})();
@@ -175,9 +202,17 @@ const theming = plugin.withOptions((options) => ({
(options) =>
{
return {
theme : {
theme: {
extend: {
colors: generateVariableColors(options.themes.default)
/**
* Add 'Primary', 'Accent' and 'Warn' palettes as colors so all color utilities
* are generated for them; "bg-primary", "text-on-primary", "bg-accent-600" etc.
* This will also allow using arbitrary values with them such as opacity and such.
*/
colors: _.fromPairs(_.flatten(_.map(_.keys(flattenColorPalette(normalizeTheme(options.themes.default))), (name) => [
[name, `rgba(var(--fuse-${name}-rgb), <alpha-value>)`],
[`on-${name}`, `rgba(var(--fuse-on-${name}-rgb), <alpha-value>)`]
])))
},
fuse : {
customProps: {
@@ -185,47 +220,45 @@ const theming = plugin.withOptions((options) => ({
light: {
'bg-app-bar' : '#FFFFFF',
'bg-card' : '#FFFFFF',
'bg-default' : colors.blueGray[100],
'bg-default' : colors.slate[100],
'bg-dialog' : '#FFFFFF',
'bg-hover' : chroma(colors.blueGray[400]).alpha(0.12).css(),
'bg-status-bar': colors.blueGray[300]
'bg-hover' : chroma(colors.slate[400]).alpha(0.12).css(),
'bg-status-bar': colors.slate[300]
},
dark : {
'bg-app-bar' : colors.blueGray[900],
'bg-card' : colors.blueGray[800],
'bg-default' : colors.blueGray[900],
'bg-dialog' : colors.blueGray[800],
'bg-app-bar' : colors.slate[900],
'bg-card' : colors.slate[800],
'bg-default' : colors.slate[900],
'bg-dialog' : colors.slate[800],
'bg-hover' : 'rgba(255, 255, 255, 0.05)',
'bg-status-bar': colors.blueGray[900]
'bg-status-bar': colors.slate[900]
}
},
foreground: {
light: {
'text-default' : colors.blueGray[800],
'text-secondary': colors.blueGray[500],
'text-hint' : colors.blueGray[400],
'text-disabled' : colors.blueGray[400],
'border' : colors.blueGray[200],
'divider' : colors.blueGray[200],
'icon' : colors.blueGray[500],
'mat-icon' : colors.blueGray[500]
'text-default' : colors.slate[800],
'text-secondary': colors.slate[500],
'text-hint' : colors.slate[400],
'text-disabled' : colors.slate[400],
'border' : colors.slate[200],
'divider' : colors.slate[200],
'icon' : colors.slate[500],
'mat-icon' : colors.slate[500]
},
dark : {
'text-default' : '#FFFFFF',
'text-secondary': colors.blueGray[400],
'text-hint' : colors.blueGray[500],
'text-disabled' : colors.blueGray[600],
'border' : chroma(colors.blueGray[100]).alpha(0.12).css(),
'divider' : chroma(colors.blueGray[100]).alpha(0.12).css(),
'icon' : colors.blueGray[400],
'mat-icon' : colors.blueGray[400]
'text-secondary': colors.slate[400],
'text-hint' : colors.slate[500],
'text-disabled' : colors.slate[600],
'border' : chroma(colors.slate[100]).alpha(0.12).css(),
'divider' : chroma(colors.slate[100]).alpha(0.12).css(),
'icon' : colors.slate[400],
'mat-icon' : colors.slate[400]
}
}
},
themes : generateThemesObject(options.themes)
}
}
},
variants: {}
}
};
}
);

View File

@@ -1,6 +1,6 @@
const plugin = require('tailwindcss/plugin');
const utilities = plugin(({
module.exports = plugin(({
addComponents
}) =>
{
@@ -54,9 +54,6 @@ const utilities = plugin(({
'--tw-ring-opacity': '1 !important',
'--tw-ring-color' : 'rgba(var(--fuse-bg-card-rgb), var(--tw-ring-opacity)) !important'
}
},
{
variants: ['dark', 'responsive', 'group-hover', 'hover']
}
);
@@ -65,11 +62,6 @@ const utilities = plugin(({
'.bg-hover': {
backgroundColor: 'var(--fuse-bg-hover) !important'
}
},
{
variants: ['dark', 'group-hover', 'hover']
}
);
});
module.exports = utilities;

View File

@@ -0,0 +1,67 @@
const _ = require('lodash');
module.exports = (data) =>
{
if ( !data )
{
return;
}
data = JSON.parse(data);
const getSCSS = (chunk) =>
{
let scss = '';
if ( typeof chunk === "object" && !Array.isArray(chunk) )
{
_.mapKeys(chunk, (value, key) =>
{
scss += key + ': ';
if ( typeof value === "object" )
{
if ( Array.isArray(value) )
{
scss += '(';
_.each(value, (val1) =>
{
if ( Array.isArray(val1) )
{
_.each(val1, (val2) =>
{
scss += val2 + ' ';
});
scss = scss.slice(0, -1) + ', ';
}
else
{
scss += val1 + ', ';
}
});
scss = scss.slice(0, -2);
scss += ')';
}
else
{
scss += '(' + getSCSS(value) + ')';
}
}
else
{
scss += getSCSS(value);
}
scss += ', ';
});
scss = scss.slice(0, -2);
}
else
{
scss += chunk;
}
return scss;
};
return '$' + getSCSS(data) + ';';
};

View File

@@ -1,3 +1,3 @@
import { Version } from '@fuse/version/version';
export const FUSE_VERSION = new Version('13.5.0').full;
export const FUSE_VERSION = new Version('17.2.0').full;

View File

@@ -3,4 +3,4 @@
flex: 1 1 auto;
width: 100%;
height: 100%;
}
}

View File

@@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ExtraOptions, PreloadAllModules, RouterModule } from '@angular/router';
import { MarkdownModule } from 'ngx-markdown';
import { FuseModule } from '@fuse';
import { FuseConfigModule } from '@fuse/services/config';
import { FuseMockApiModule } from '@fuse/lib/mock-api';
@@ -36,10 +35,7 @@ const routerConfig: ExtraOptions = {
CoreModule,
// Layout module of your application
LayoutModule,
// 3rd party modules that require global configuration via forRoot
MarkdownModule.forRoot({})
LayoutModule
],
bootstrap : [
AppComponent

View File

@@ -4,8 +4,8 @@ import { forkJoin, Observable } from 'rxjs';
import { MessagesService } from 'app/layout/common/messages/messages.service';
import { NavigationService } from 'app/core/navigation/navigation.service';
import { NotificationsService } from 'app/layout/common/notifications/notifications.service';
import { QuickChatService } from 'app/layout/common/quick-chat/quick-chat.service';
import { ShortcutsService } from 'app/layout/common/shortcuts/shortcuts.service';
import { UserService } from 'app/core/user/user.service';
@Injectable({
providedIn: 'root'
@@ -19,8 +19,8 @@ export class InitialDataResolver implements Resolve<any>
private _messagesService: MessagesService,
private _navigationService: NavigationService,
private _notificationsService: NotificationsService,
private _shortcutsService: ShortcutsService,
private _userService: UserService
private _quickChatService: QuickChatService,
private _shortcutsService: ShortcutsService
)
{
}
@@ -42,8 +42,8 @@ export class InitialDataResolver implements Resolve<any>
this._navigationService.get(),
this._messagesService.getAll(),
this._notificationsService.getAll(),
this._shortcutsService.getAll(),
this._userService.get()
this._quickChatService.getChats(),
this._shortcutsService.getAll()
]);
}
}

View File

@@ -9,21 +9,20 @@ import { InitialDataResolver } from 'app/app.resolvers';
/* eslint-disable @typescript-eslint/explicit-function-return-type */
export const appRoutes: Route[] = [
// Redirect empty path to '/dashboards/project'
{path: '', pathMatch : 'full', redirectTo: 'dashboards/project'},
// Redirect empty path to '/example'
{path: '', pathMatch : 'full', redirectTo: 'example'},
// Redirect signed in user to the '/dashboards/project'
// Redirect signed-in user to the '/example'
//
// After the user signs in, the sign in page will redirect the user to the 'signed-in-redirect'
// After the user signs in, the sign-in page will redirect the user to the 'signed-in-redirect'
// path. Below is another redirection for that path to redirect the user to the desired
// location. This is a small convenience to keep all main routes together here on this file.
{path: 'signed-in-redirect', pathMatch : 'full', redirectTo: 'dashboards/project'},
{path: 'signed-in-redirect', pathMatch : 'full', redirectTo: 'example'},
// Auth routes for guests
{
path: '',
canActivate: [NoAuthGuard],
canActivateChild: [NoAuthGuard],
canMatch: [NoAuthGuard],
component: LayoutComponent,
data: {
layout: 'empty'
@@ -40,8 +39,7 @@ export const appRoutes: Route[] = [
// Auth routes for authenticated users
{
path: '',
canActivate: [AuthGuard],
canActivateChild: [AuthGuard],
canMatch: [AuthGuard],
component: LayoutComponent,
data: {
layout: 'empty'
@@ -55,156 +53,25 @@ export const appRoutes: Route[] = [
// Landing routes
{
path: '',
component : LayoutComponent,
component: LayoutComponent,
data: {
layout: 'empty'
},
children : [
children: [
{path: 'home', loadChildren: () => import('app/modules/landing/home/home.module').then(m => m.LandingHomeModule)},
]
},
// Admin routes
{
path : '',
canActivate: [AuthGuard],
canActivateChild: [AuthGuard],
component : LayoutComponent,
resolve : {
path: '',
canMatch: [AuthGuard],
component: LayoutComponent,
resolve: {
initialData: InitialDataResolver,
},
children : [
// Dashboards
{path: 'dashboards', children: [
{path: 'project', loadChildren: () => import('app/modules/admin/dashboards/project/project.module').then(m => m.ProjectModule)},
{path: 'analytics', loadChildren: () => import('app/modules/admin/dashboards/analytics/analytics.module').then(m => m.AnalyticsModule)},
{path: 'finance', loadChildren: () => import('app/modules/admin/dashboards/finance/finance.module').then(m => m.FinanceModule)},
{path: 'crypto', loadChildren: () => import('app/modules/admin/dashboards/crypto/crypto.module').then(m => m.CryptoModule)},
]},
// Apps
{path: 'apps', children: [
{path: 'academy', loadChildren: () => import('app/modules/admin/apps/academy/academy.module').then(m => m.AcademyModule)},
{path: 'calendar', loadChildren: () => import('app/modules/admin/apps/calendar/calendar.module').then(m => m.CalendarModule)},
{path: 'chat', loadChildren: () => import('app/modules/admin/apps/chat/chat.module').then(m => m.ChatModule)},
{path: 'contacts', loadChildren: () => import('app/modules/admin/apps/contacts/contacts.module').then(m => m.ContactsModule)},
{path: 'ecommerce', loadChildren: () => import('app/modules/admin/apps/ecommerce/ecommerce.module').then(m => m.ECommerceModule)},
{path: 'file-manager', loadChildren: () => import('app/modules/admin/apps/file-manager/file-manager.module').then(m => m.FileManagerModule)},
{path: 'help-center', loadChildren: () => import('app/modules/admin/apps/help-center/help-center.module').then(m => m.HelpCenterModule)},
{path: 'mailbox', loadChildren: () => import('app/modules/admin/apps/mailbox/mailbox.module').then(m => m.MailboxModule)},
{path: 'notes', loadChildren: () => import('app/modules/admin/apps/notes/notes.module').then(m => m.NotesModule)},
{path: 'scrumboard', loadChildren: () => import('app/modules/admin/apps/scrumboard/scrumboard.module').then(m => m.ScrumboardModule)},
{path: 'tasks', loadChildren: () => import('app/modules/admin/apps/tasks/tasks.module').then(m => m.TasksModule)},
]},
// Pages
{path: 'pages', children: [
// Activities
{path: 'activities', loadChildren: () => import('app/modules/admin/pages/activities/activities.module').then(m => m.ActivitiesModule)},
// Authentication
{path: 'authentication', loadChildren: () => import('app/modules/admin/pages/authentication/authentication.module').then(m => m.AuthenticationModule)},
// Coming Soon
{path: 'coming-soon', loadChildren: () => import('app/modules/admin/pages/coming-soon/coming-soon.module').then(m => m.ComingSoonModule)},
// Error
{path: 'error', children: [
{path: '404', loadChildren: () => import('app/modules/admin/pages/error/error-404/error-404.module').then(m => m.Error404Module)},
{path: '500', loadChildren: () => import('app/modules/admin/pages/error/error-500/error-500.module').then(m => m.Error500Module)}
]},
// Invoice
{path: 'invoice', children: [
{path: 'printable', children: [
{path: 'compact', loadChildren: () => import('app/modules/admin/pages/invoice/printable/compact/compact.module').then(m => m.CompactModule)},
{path: 'modern', loadChildren: () => import('app/modules/admin/pages/invoice/printable/modern/modern.module').then(m => m.ModernModule)}
]}
]},
// Maintenance
{path: 'maintenance', loadChildren: () => import('app/modules/admin/pages/maintenance/maintenance.module').then(m => m.MaintenanceModule)},
// Pricing
{path: 'pricing', children: [
{path: 'modern', loadChildren: () => import('app/modules/admin/pages/pricing/modern/modern.module').then(m => m.PricingModernModule)},
{path: 'simple', loadChildren: () => import('app/modules/admin/pages/pricing/simple/simple.module').then(m => m.PricingSimpleModule)},
{path: 'single', loadChildren: () => import('app/modules/admin/pages/pricing/single/single.module').then(m => m.PricingSingleModule)},
{path: 'table', loadChildren: () => import('app/modules/admin/pages/pricing/table/table.module').then(m => m.PricingTableModule)}
]},
// Profile
{path: 'profile', loadChildren: () => import('app/modules/admin/pages/profile/profile.module').then(m => m.ProfileModule)},
// Settings
{path: 'settings', loadChildren: () => import('app/modules/admin/pages/settings/settings.module').then(m => m.SettingsModule)},
]},
// User Interface
{path: 'ui', children: [
// Material Components
{path: 'material-components', loadChildren: () => import('app/modules/admin/ui/material-components/material-components.module').then(m => m.MaterialComponentsModule)},
// Fuse Components
{path: 'fuse-components', loadChildren: () => import('app/modules/admin/ui/fuse-components/fuse-components.module').then(m => m.FuseComponentsModule)},
// Other Components
{path: 'other-components', loadChildren: () => import('app/modules/admin/ui/other-components/other-components.module').then(m => m.OtherComponentsModule)},
// TailwindCSS
{path: 'tailwindcss', loadChildren: () => import('app/modules/admin/ui/tailwindcss/tailwindcss.module').then(m => m.TailwindCSSModule)},
// Advanced Search
{path: 'advanced-search', loadChildren: () => import('app/modules/admin/ui/advanced-search/advanced-search.module').then(m => m.AdvancedSearchModule)},
// Animations
{path: 'animations', loadChildren: () => import('app/modules/admin/ui/animations/animations.module').then(m => m.AnimationsModule)},
// Cards
{path: 'cards', loadChildren: () => import('app/modules/admin/ui/cards/cards.module').then(m => m.CardsModule)},
// Colors
{path: 'colors', loadChildren: () => import('app/modules/admin/ui/colors/colors.module').then(m => m.ColorsModule)},
// Confirmation Dialog
{path: 'confirmation-dialog', loadChildren: () => import('app/modules/admin/ui/confirmation-dialog/confirmation-dialog.module').then(m => m.ConfirmationDialogModule)},
// Datatable
{path: 'datatable', loadChildren: () => import('app/modules/admin/ui/datatable/datatable.module').then(m => m.DatatableModule)},
// Forms
{path: 'forms', children: [
{path: 'fields', loadChildren: () => import('app/modules/admin/ui/forms/fields/fields.module').then(m => m.FormsFieldsModule)},
{path: 'layouts', loadChildren: () => import('app/modules/admin/ui/forms/layouts/layouts.module').then(m => m.FormsLayoutsModule)},
{path: 'wizards', loadChildren: () => import('app/modules/admin/ui/forms/wizards/wizards.module').then(m => m.FormsWizardsModule)}
]},
// Icons
{path: 'icons', loadChildren: () => import('app/modules/admin/ui/icons/icons.module').then(m => m.IconsModule)},
// Page Layouts
{path: 'page-layouts', loadChildren: () => import('app/modules/admin/ui/page-layouts/page-layouts.module').then(m => m.PageLayoutsModule)},
// Typography
{path: 'typography', loadChildren: () => import('app/modules/admin/ui/typography/typography.module').then(m => m.TypographyModule)}
]},
// Documentation
{path: 'docs', children: [
// Changelog
{path: 'changelog', loadChildren: () => import('app/modules/admin/docs/changelog/changelog.module').then(m => m.ChangelogModule)},
// Guides
{path: 'guides', loadChildren: () => import('app/modules/admin/docs/guides/guides.module').then(m => m.GuidesModule)}
]},
// 404 & Catch all
{path: '404-not-found', pathMatch: 'full', loadChildren: () => import('app/modules/admin/pages/error/error-404/error-404.module').then(m => m.Error404Module)},
{path: '**', redirectTo: '404-not-found'}
children: [
{path: 'example', loadChildren: () => import('app/modules/admin/example/example.module').then(m => m.ExampleModule)},
]
}
];

View File

@@ -1,7 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { catchError, Observable, throwError } from 'rxjs';
import { AuthService } from 'app/core/auth/auth.service';
import { AuthUtils } from 'app/core/auth/auth.utils';

View File

@@ -1,7 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { catchError, Observable, of, switchMap, throwError } from 'rxjs';
import { AuthUtils } from 'app/core/auth/auth.utils';
import { UserService } from 'app/core/user/user.service';
@@ -97,8 +96,8 @@ export class AuthService
*/
signInUsingToken(): Observable<any>
{
// Renew token
return this._httpClient.post('api/auth/refresh-access-token', {
// Sign in using the token
return this._httpClient.post('api/auth/sign-in-with-token', {
accessToken: this.accessToken
}).pipe(
catchError(() =>
@@ -108,8 +107,17 @@ export class AuthService
),
switchMap((response: any) => {
// Store the access token in the local storage
this.accessToken = response.accessToken;
// Replace the access token with the new one if it's available on
// the response object.
//
// This is an added optional step for better security. Once you sign
// in using the token, you should generate a new one on the server
// side and attach it to the response object. Then the following
// piece of code can replace the token with the refreshed one.
if ( response.accessToken )
{
this.accessToken = response.accessToken;
}
// Set the authenticated flag to true
this._authenticated = true;

View File

@@ -1,13 +1,12 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, CanLoad, Route, Router, RouterStateSnapshot, UrlSegment, UrlTree } from '@angular/router';
import { Observable, of } from 'rxjs';
import { CanMatch, Route, Router, UrlSegment, UrlTree } from '@angular/router';
import { Observable, of, switchMap } from 'rxjs';
import { AuthService } from 'app/core/auth/auth.service';
import { switchMap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad
export class AuthGuard implements CanMatch
{
/**
* Constructor
@@ -24,38 +23,14 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad
// -----------------------------------------------------------------------------------------------------
/**
* Can activate
*
* @param route
* @param state
*/
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean
{
const redirectUrl = state.url === '/sign-out' ? '/' : state.url;
return this._check(redirectUrl);
}
/**
* Can activate child
*
* @param childRoute
* @param state
*/
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
{
const redirectUrl = state.url === '/sign-out' ? '/' : state.url;
return this._check(redirectUrl);
}
/**
* Can load
* Can match
*
* @param route
* @param segments
*/
canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean
canMatch(route: Route, segments: UrlSegment[]): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
{
return this._check('/');
return this._check(segments);
}
// -----------------------------------------------------------------------------------------------------
@@ -65,29 +40,28 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad
/**
* Check the authenticated status
*
* @param redirectURL
* @param segments
* @private
*/
private _check(redirectURL: string): Observable<boolean>
private _check(segments: UrlSegment[]): Observable<boolean | UrlTree>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
return this._authService.check().pipe(
switchMap((authenticated) => {
// If the user is not authenticated...
if ( !authenticated )
{
// Redirect to the sign-in page
this._router.navigate(['sign-in'], {queryParams: {redirectURL}});
// If the user is not authenticated...
if ( !authenticated )
{
// Redirect to the sign-in page with a redirectUrl param
const redirectURL = `/${segments.join('/')}`;
const urlTree = this._router.parseUrl(`sign-in?redirectURL=${redirectURL}`);
// Prevent the access
return of(false);
}
return of(urlTree);
}
// Allow the access
return of(true);
})
);
// Allow the access
return of(true);
})
);
}
}

View File

@@ -1,13 +1,12 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, CanLoad, Route, Router, RouterStateSnapshot, UrlSegment, UrlTree } from '@angular/router';
import { Observable, of } from 'rxjs';
import { CanMatch, Route, Router, UrlSegment, UrlTree } from '@angular/router';
import { Observable, of, switchMap } from 'rxjs';
import { AuthService } from 'app/core/auth/auth.service';
import { switchMap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class NoAuthGuard implements CanActivate, CanActivateChild, CanLoad
export class NoAuthGuard implements CanMatch
{
/**
* Constructor
@@ -24,34 +23,12 @@ export class NoAuthGuard implements CanActivate, CanActivateChild, CanLoad
// -----------------------------------------------------------------------------------------------------
/**
* Can activate
*
* @param route
* @param state
*/
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean
{
return this._check();
}
/**
* Can activate child
*
* @param childRoute
* @param state
*/
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
{
return this._check();
}
/**
* Can load
* Can match
*
* @param route
* @param segments
*/
canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean
canMatch(route: Route, segments: UrlSegment[]): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
{
return this._check();
}
@@ -67,24 +44,10 @@ export class NoAuthGuard implements CanActivate, CanActivateChild, CanLoad
*/
private _check(): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is authenticated...
if ( authenticated )
{
// Redirect to the root
this._router.navigate(['']);
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
// Check the authentication status and return an observable of
// "true" or "false" to allow or prevent the access
return this._authService.check().pipe(
switchMap((authenticated) => of(!authenticated))
);
}
}

View File

@@ -2,7 +2,9 @@ import { Layout } from 'app/layout/layout.types';
// Types
export type Scheme = 'auto' | 'dark' | 'light';
export type Theme = 'default' | string;
export type Screens = { [key: string]: string };
export type Theme = 'theme-default' | string;
export type Themes = { id: string; name: string }[];
/**
* AppConfig interface. Update this interface to strictly type your config
@@ -12,7 +14,9 @@ export interface AppConfig
{
layout: Layout;
scheme: Scheme;
screens: Screens;
theme: Theme;
themes: Themes;
}
/**
@@ -22,9 +26,46 @@ export interface AppConfig
* If you need to store global configuration for your app, you can use this
* object to set the defaults. To access, update and reset the config, use
* FuseConfigService and its methods.
*
* "Screens" are carried over to the BreakpointObserver for accessing them within
* components, and they are required.
*
* "Themes" are required for Tailwind to generate themes.
*/
export const appConfig: AppConfig = {
layout: 'classy',
scheme: 'light',
theme : 'default'
layout : 'classy',
scheme : 'light',
screens: {
sm: '600px',
md: '960px',
lg: '1280px',
xl: '1440px'
},
theme : 'theme-default',
themes : [
{
id : 'theme-default',
name: 'Default'
},
{
id : 'theme-brand',
name: 'Brand'
},
{
id : 'theme-teal',
name: 'Teal'
},
{
id : 'theme-rose',
name: 'Rose'
},
{
id : 'theme-purple',
name: 'Purple'
},
{
id : 'theme-amber',
name: 'Amber'
}
]
};

View File

@@ -17,7 +17,6 @@ export class IconsModule
this._matIconRegistry.addSvgIconSet(this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-twotone.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('mat_outline', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-outline.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('mat_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-solid.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('iconsmind', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/iconsmind.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('feather', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/feather.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_outline', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-outline.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-solid.svg'));

View File

@@ -1,7 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Observable, ReplaySubject, tap } from 'rxjs';
import { Navigation } from 'app/core/navigation/navigation.types';
@Injectable({

View File

@@ -1,6 +1,5 @@
import { Translation, TRANSLOCO_CONFIG, TRANSLOCO_LOADER, translocoConfig, TranslocoModule, TranslocoService } from '@ngneat/transloco';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { environment } from 'environments/environment';
import { TranslocoHttpLoader } from 'app/core/transloco/transloco.http-loader';
@NgModule({
@@ -25,7 +24,7 @@ import { TranslocoHttpLoader } from 'app/core/transloco/transloco.http-loader';
defaultLang : 'en',
fallbackLang : 'en',
reRenderOnLangChange: true,
prodMode : environment.production
prodMode : true
})
},
{

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