diff --git a/package.json b/package.json index dd64bf5e..c96e627d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fuse/demo", - "version": "12.2.0", + "version": "12.3.0", "license": "https://themeforest.net/licenses/standard", "private": true, "scripts": { diff --git a/src/@fuse/components/masonry/index.ts b/src/@fuse/components/masonry/index.ts new file mode 100644 index 00000000..ac978a78 --- /dev/null +++ b/src/@fuse/components/masonry/index.ts @@ -0,0 +1 @@ +export * from '@fuse/components/card/public-api'; diff --git a/src/@fuse/components/masonry/masonry.component.html b/src/@fuse/components/masonry/masonry.component.html new file mode 100644 index 00000000..4b0a6719 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/@fuse/components/masonry/masonry.component.scss b/src/@fuse/components/masonry/masonry.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/@fuse/components/masonry/masonry.component.ts b/src/@fuse/components/masonry/masonry.component.ts new file mode 100644 index 00000000..5cc91ec4 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.component.ts @@ -0,0 +1,87 @@ +import { AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { FuseAnimations } from '@fuse/animations'; +import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; + +@Component({ + selector : 'fuse-masonry', + templateUrl : './masonry.component.html', + styleUrls : ['./masonry.component.scss'], + encapsulation: ViewEncapsulation.None, + animations : FuseAnimations, + exportAs : 'fuseMasonry' +}) +export class FuseMasonryComponent implements OnChanges, AfterViewInit +{ + @Input() columnsTemplate: TemplateRef; + @Input() columns: number; + @Input() items: any[] = []; + distributedColumns: any[] = []; + + /** + * Constructor + */ + constructor(private _fuseMediaWatcherService: FuseMediaWatcherService) + { + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On changes + * + * @param changes + */ + ngOnChanges(changes: SimpleChanges): void + { + // Columns + if ( 'columns' in changes ) + { + // Distribute the items + this._distributeItems(); + } + + // Items + if ( 'items' in changes ) + { + // Distribute the items + this._distributeItems(); + } + } + + /** + * After view init + */ + ngAfterViewInit(): void + { + // Distribute the items for the first time + this._distributeItems(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Private methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Distribute items into columns + */ + private _distributeItems(): void + { + // Return an empty array if there are no items + if ( this.items.length === 0 ) + { + this.distributedColumns = []; + return; + } + + // Prepare the distributed columns array + this.distributedColumns = Array.from(Array(this.columns), item => ({items: []})); + + // Distribute the items to columns + for ( let i = 0; i < this.items.length; i++ ) + { + this.distributedColumns[i % this.columns].items.push(this.items[i]); + } + } +} diff --git a/src/@fuse/components/masonry/masonry.module.ts b/src/@fuse/components/masonry/masonry.module.ts new file mode 100644 index 00000000..651c5510 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FuseMasonryComponent } from '@fuse/components/masonry/masonry.component'; + +@NgModule({ + declarations: [ + FuseMasonryComponent + ], + imports : [ + CommonModule + ], + exports : [ + FuseMasonryComponent + ] +}) +export class FuseMasonryModule +{ +} diff --git a/src/@fuse/components/masonry/public-api.ts b/src/@fuse/components/masonry/public-api.ts new file mode 100644 index 00000000..e074c48c --- /dev/null +++ b/src/@fuse/components/masonry/public-api.ts @@ -0,0 +1,2 @@ +export * from '@fuse/components/masonry/masonry.component'; +export * from '@fuse/components/masonry/masonry.module'; diff --git a/src/@fuse/styles/components/example-viewer.scss b/src/@fuse/styles/components/example-viewer.scss index cdee5c7e..fe0fd303 100644 --- a/src/@fuse/styles/components/example-viewer.scss +++ b/src/@fuse/styles/components/example-viewer.scss @@ -36,7 +36,6 @@ .mat-tab-body-content { .fuse-highlight { - margin: -24px; pre { margin: 0; diff --git a/src/@fuse/version/fuse-version.ts b/src/@fuse/version/fuse-version.ts index 3b764131..a43d2acb 100644 --- a/src/@fuse/version/fuse-version.ts +++ b/src/@fuse/version/fuse-version.ts @@ -1,4 +1,4 @@ import { Version } from '@fuse/version/version'; -const __FUSE_VERSION__ = '12.2.0'; +const __FUSE_VERSION__ = '12.3.0'; export const FUSE_VERSION = new Version(__FUSE_VERSION__).full; diff --git a/src/app/mock-api/apps/notes/api.ts b/src/app/mock-api/apps/notes/api.ts new file mode 100644 index 00000000..fe80771d --- /dev/null +++ b/src/app/mock-api/apps/notes/api.ts @@ -0,0 +1,264 @@ +import { Injectable } from '@angular/core'; +import { cloneDeep } from 'lodash-es'; +import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service'; +import { labels as labelsData, notes as notesData } from 'app/mock-api/apps/notes/data'; +import { FuseMockApiUtils } from '@fuse/lib/mock-api'; + +@Injectable({ + providedIn: 'root' +}) +export class NotesMockApi +{ + private _labels: any[] = labelsData; + private _notes: any[] = notesData; + + /** + * Constructor + */ + constructor(private _fuseMockApiService: FuseMockApiService) + { + // Register Mock API handlers + this.registerHandlers(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Register Mock API handlers + */ + registerHandlers(): void + { + // ----------------------------------------------------------------------------------------------------- + // @ Labels - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/notes/labels') + .reply(() => { + + return [ + 200, + cloneDeep(this._labels) + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Labels - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/notes/labels') + .reply(({request}) => { + + // Create a new label + const label = { + id : FuseMockApiUtils.guid(), + title: request.body.title + }; + + // Update the labels + this._labels.push(label); + + return [ + 200, + cloneDeep(this._labels) + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Labels - PATCH + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPatch('api/apps/notes/labels') + .reply(({request}) => { + + // Get label + const updatedLabel = request.body.label; + + // Update the label + this._labels = this._labels.map((label) => { + if ( label.id === updatedLabel.id ) + { + return { + ...label, + title: updatedLabel.title + }; + } + + return label; + }); + + return [ + 200, + cloneDeep(this._labels) + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Labels - DELETE + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onDelete('api/apps/notes/labels') + .reply(({request}) => { + + // Get label id + const id = request.params.get('id'); + + // Delete the label + this._labels = this._labels.filter((label) => label.id !== id); + + // Go through notes and delete the label + this._notes = this._notes.map((note) => ({ + ...note, + labels: note.labels.filter((item) => item !== id) + })); + + return [ + 200, + cloneDeep(this._labels) + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Note Tasks - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/notes/tasks') + .reply(({request}) => { + + // Get note and task + let updatedNote = request.body.note; + const task = request.body.task; + + // Update the note + this._notes = this._notes.map((note) => { + if ( note.id === updatedNote.id ) + { + // Update the tasks + if ( !note.tasks ) + { + note.tasks = []; + } + + note.tasks.push({ + id : FuseMockApiUtils.guid(), + content : task, + completed: false + }); + + // Update the updatedNote with the new task + updatedNote = cloneDeep(note); + + return { + ...note + }; + } + + return note; + }); + + return [ + 200, + updatedNote + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Notes - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/notes/all') + .reply(() => { + + // Clone the labels and notes + const labels = cloneDeep(this._labels); + let notes = cloneDeep(this._notes); + + // Attach the labels to the notes + notes = notes.map((note) => ( + { + ...note, + labels: note.labels.map((labelId) => labels.find((label) => label.id === labelId)) + } + )); + + return [ + 200, + notes + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Notes - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/notes') + .reply(({request}) => { + + // Get note + const note = request.body.note; + + // Add an id + note.id = FuseMockApiUtils.guid(); + + // Push the note + this._notes.push(note); + + return [ + 200, + note + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Notes - PATCH + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPatch('api/apps/notes') + .reply(({request}) => { + + // Get note + const updatedNote = request.body.updatedNote; + + // Update the note + this._notes = this._notes.map((note) => { + if ( note.id === updatedNote.id ) + { + return { + ...updatedNote + }; + } + + return note; + }); + + return [ + 200, + updatedNote + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Notes - DELETE + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onDelete('api/apps/notes') + .reply(({request}) => { + + // Get the id + const id = request.params.get('id'); + + // Find the note and delete it + this._notes.forEach((item, index) => { + + if ( item.id === id ) + { + this._notes.splice(index, 1); + } + }); + + // Return the response + return [200, true]; + }); + } +} diff --git a/src/app/mock-api/apps/notes/data.ts b/src/app/mock-api/apps/notes/data.ts new file mode 100644 index 00000000..f10717c4 --- /dev/null +++ b/src/app/mock-api/apps/notes/data.ts @@ -0,0 +1,314 @@ +/* tslint:disable:max-line-length */ +import moment from 'moment'; + +export const labels = [ + { + id : 'f47c92e5-20b9-44d9-917f-9ff4ad25dfd0', + title: 'Family' + }, + { + id : 'e2f749f5-41ed-49d0-a92a-1c83d879e371', + title: 'Work' + }, + { + id : 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528', + title: 'Tasks' + }, + { + id : '6c288794-47eb-4605-8bdf-785b61a449d3', + title: 'Priority' + }, + { + id : 'bbc73458-940b-421c-8d5f-8dcd23a9b0d6', + title: 'Personal' + }, + { + id : '2dc11344-3507-48e0-83d6-1c047107f052', + title: 'Friends' + } +]; + +export const notes = [ + { + id : '8f011ac5-b71c-4cd7-a317-857dcd7d85e0', + title : '', + content : 'Find a new company name', + tasks : null, + image : null, + reminder : null, + labels : ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], + archived : false, + createdAt: moment().hour(10).minute(19).subtract(98, 'day').toISOString(), + updatedAt: null + }, + { + id : 'ced0a1ce-051d-41a3-b080-e2161e4ae621', + title : '', + content : 'Send the photos of last summer to John', + tasks : null, + image : 'assets/images/cards/14-640x480.jpg', + reminder : null, + labels : [ + 'bbc73458-940b-421c-8d5f-8dcd23a9b0d6', + 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528' + ], + archived : false, + createdAt: moment().hour(15).minute(37).subtract(80, 'day').toISOString(), + updatedAt: null + }, + { + id : 'd3ac02a9-86e4-4187-bbd7-2c965518b3a3', + title : '', + content : 'Update the design of the theme', + tasks : null, + image : null, + reminder : null, + labels : ['6c288794-47eb-4605-8bdf-785b61a449d3'], + archived : false, + createdAt: moment().hour(19).minute(27).subtract(74, 'day').toISOString(), + updatedAt: moment().hour(15).minute(36).subtract(50, 'day').toISOString() + }, + { + id : '89861bd4-0144-4bb4-8b39-332ca10371d5', + title : '', + content : 'Theming support for all apps', + tasks : null, + image : null, + reminder : moment().hour(12).minute(34).add(50, 'day').toISOString(), + labels : ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], + archived : false, + createdAt: moment().hour(12).minute(34).subtract(59, 'day').toISOString(), + updatedAt: null + }, + { + id : 'ffd20f3c-2d43-4c6b-8021-278032fc9e92', + title : 'Gift Ideas', + content : 'Stephanie\'s birthday is coming and I need to pick a present for her. Take a look at the below list and buy one of them (or all of them)', + tasks : [ + { + id : '330a924f-fb51-48f6-a374-1532b1dd353d', + content : 'Scarf', + completed: false + }, + { + id : '781855a6-2ad2-4df4-b0af-c3cb5f302b40', + content : 'A new bike helmet', + completed: true + }, + { + id : 'bcb8923b-33cd-42c2-9203-170994fa24f5', + content : 'Necklace', + completed: false + }, + { + id : '726bdf6e-5cd7-408a-9a4f-0d7bb98c1c4b', + content : 'Flowers', + completed: false + } + ], + image : null, + reminder : null, + labels : ['f47c92e5-20b9-44d9-917f-9ff4ad25dfd0'], + archived : false, + createdAt: moment().hour(16).minute(4).subtract(47, 'day').toISOString(), + updatedAt: null + }, + { + id : '71d223bb-abab-4183-8919-cd3600a950b4', + title : 'Shopping list', + content : '', + tasks : [ + { + id : 'e3cbc986-641c-4448-bc26-7ecfa0549c22', + content : 'Bread', + completed: true + }, + { + id : '34013111-ab2c-4b2f-9352-d2ae282f57d3', + content : 'Milk', + completed: false + }, + { + id : '0fbdea82-cc79-4433-8ee4-54fd542c380d', + content : 'Onions', + completed: false + }, + { + id : '66490222-743e-4262-ac91-773fcd98a237', + content : 'Coffee', + completed: true + }, + { + id : 'ab367215-d06a-48b0-a7b8-e161a63b07bd', + content : 'Toilet Paper', + completed: true + } + ], + image : null, + reminder : moment().hour(10).minute(44).subtract(35, 'day').toISOString(), + labels : ['b1cde9ee-e54d-4142-ad8b-cf55dafc9528'], + archived : false, + createdAt: moment().hour(10).minute(44).subtract(35, 'day').toISOString(), + updatedAt: null + }, + { + id : '11fbeb98-ae5e-41ad-bed6-330886fd7906', + title : 'Keynote Schedule', + content : '', + tasks : [ + { + id : '2711bac1-7d8a-443a-a4fe-506ef51d3fcb', + content : 'Breakfast', + completed: true + }, + { + id : 'e3a2d675-a3e5-4cef-9205-feeccaf949d7', + content : 'Opening ceremony', + completed: true + }, + { + id : '7a721b6d-9d85-48e0-b6c3-f927079af582', + content : 'Talk 1: How we did it!', + completed: true + }, + { + id : 'bdb4d5cd-5bb8-45e2-9186-abfd8307e429', + content : 'Talk 2: How can you do it!', + completed: false + }, + { + id : 'c8293bb4-8ab4-4310-bbc2-52ecf8ec0c54', + content : 'Lunch break', + completed: false + } + ], + image : null, + reminder : moment().hour(11).minute(27).subtract(14, 'day').toISOString(), + labels : [ + 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528', + 'e2f749f5-41ed-49d0-a92a-1c83d879e371' + ], + archived : false, + createdAt: moment().hour(11).minute(27).subtract(24, 'day').toISOString(), + updatedAt: null + }, + { + id : 'd46dee8b-8761-4b6d-a1df-449d6e6feb6a', + title : '', + content : 'Organize the dad\'s surprise retirement party', + tasks : null, + image : null, + reminder : moment().hour(14).minute(56).subtract(25, 'day').toISOString(), + labels : ['f47c92e5-20b9-44d9-917f-9ff4ad25dfd0'], + archived : false, + createdAt: moment().hour(14).minute(56).subtract(20, 'day').toISOString(), + updatedAt: null + }, + { + id : '6bc9f002-1675-417c-93c4-308fba39023e', + title : 'Plan the road trip', + content : '', + tasks : null, + image : 'assets/images/cards/17-640x480.jpg', + reminder : null, + labels : [ + '2dc11344-3507-48e0-83d6-1c047107f052', + 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528' + ], + archived : false, + createdAt: moment().hour(9).minute(32).subtract(15, 'day').toISOString(), + updatedAt: moment().hour(17).minute(6).subtract(12, 'day').toISOString() + }, + { + id : '15188348-78aa-4ed6-b5c2-028a214ba987', + title : 'Office Address', + content : '933 8th Street Stamford, CT 06902', + tasks : null, + image : null, + reminder : null, + labels : ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], + archived : false, + createdAt: moment().hour(20).minute(5).subtract(12, 'day').toISOString(), + updatedAt: null + }, + { + id : '1dbfc685-1a0a-4070-9ca7-ed896c523037', + title : 'Tasks', + content : '', + tasks : [ + { + id : '004638bf-3ee6-47a5-891c-3be7b9f3df09', + content : 'Wash the dishes', + completed: true + }, + { + id : '86e6820b-1ae3-4c14-a13e-35605a0d654b', + content : 'Walk the dog', + completed: false + } + ], + image : null, + reminder : moment().hour(13).minute(43).subtract(2, 'day').toISOString(), + labels : ['bbc73458-940b-421c-8d5f-8dcd23a9b0d6'], + archived : false, + createdAt: moment().hour(13).minute(43).subtract(7, 'day').toISOString(), + updatedAt: null + }, + { + id : '49548409-90a3-44d4-9a9a-f5af75aa9a66', + title : '', + content : 'Dinner with parents', + tasks : null, + image : null, + reminder : null, + labels : [ + 'f47c92e5-20b9-44d9-917f-9ff4ad25dfd0', + '6c288794-47eb-4605-8bdf-785b61a449d3' + ], + archived : false, + createdAt: moment().hour(7).minute(12).subtract(2, 'day').toISOString(), + updatedAt: null + }, + { + id : 'c6d13a35-500d-4491-a3f3-6ca05d6632d3', + title : '', + content : 'Re-fill the medicine cabinet', + tasks : null, + image : null, + reminder : null, + labels : [ + 'bbc73458-940b-421c-8d5f-8dcd23a9b0d6', + '6c288794-47eb-4605-8bdf-785b61a449d3' + ], + archived : true, + createdAt: moment().hour(17).minute(14).subtract(100, 'day').toISOString(), + updatedAt: null + }, + { + id : 'c6d13a35-500d-4491-a3f3-6ca05d6632d3', + title : '', + content : 'Update the icons pack', + tasks : null, + image : null, + reminder : null, + labels : ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], + archived : true, + createdAt: moment().hour(10).minute(29).subtract(85, 'day').toISOString(), + updatedAt: null + }, + { + id : '46214383-f8e7-44da-aa2e-0b685e0c5027', + title : 'Team Meeting', + content : 'Talk about the future of the web apps', + tasks : null, + image : null, + reminder : null, + labels : [ + 'e2f749f5-41ed-49d0-a92a-1c83d879e371', + 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528' + ], + archived : true, + createdAt: moment().hour(15).minute(30).subtract(69, 'day').toISOString(), + updatedAt: null + } +]; diff --git a/src/app/mock-api/index.ts b/src/app/mock-api/index.ts index 4b312390..2dc55532 100644 --- a/src/app/mock-api/index.ts +++ b/src/app/mock-api/index.ts @@ -11,6 +11,7 @@ import { IconsMockApi } from 'app/mock-api/ui/icons/api'; import { MailboxMockApi } from 'app/mock-api/apps/mailbox/api'; import { MessagesMockApi } from 'app/mock-api/common/messages/api'; import { NavigationMockApi } from 'app/mock-api/common/navigation/api'; +import { NotesMockApi } from 'app/mock-api/apps/notes/api'; import { NotificationsMockApi } from 'app/mock-api/common/notifications/api'; import { ProjectMockApi } from 'app/mock-api/dashboards/project/api'; import { SearchMockApi } from 'app/mock-api/common/search/api'; @@ -32,6 +33,7 @@ export const mockApiServices = [ MailboxMockApi, MessagesMockApi, NavigationMockApi, + NotesMockApi, NotificationsMockApi, ProjectMockApi, SearchMockApi,