Adds SPFx react slide swiper sample (#412)

This commit is contained in:
Velin Georgiev 2018-02-14 14:05:00 +00:00 committed by Vesa Juvonen
parent c9a577476f
commit 8680fec4fd
29 changed files with 857 additions and 0 deletions

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

35
samples/react-slide-swiper/.gitignore vendored Normal file
View File

@ -0,0 +1,35 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts
package-lock.json

View File

@ -0,0 +1,8 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.4.0",
"libraryName": "react-slide-swiper",
"libraryId": "c7fdd51c-469e-432f-bdde-930294d3a133",
"environment": "spo"
}
}

View File

@ -0,0 +1,62 @@
# SPFx React Slide Swiper #
## Summary
This SPFx React web part sample demonstrates mobile touch slide swiper. By default the swiper web part is responsive, has cross device and browser touch support and uses paging, but additional features like navigation, autoplay, loop of the slides and more can be enabled from the web part properties panel. The swiper web part can be used as carousel as well. The slides or cards template can easily be customized. The SPFx React swiper client side solution can easly be extended with more swiper, carousel like features because it is based on a popular feature rich JavaScript library called [Swiper](https://github.com/nolimits4web/swiper).
![SPFx React Slide Swiper, can also be used as carousel](./assets/SPFx-React-Slider-Swiper.gif)
## Solution packaging and bundle optimization considerations
This sample uses the Swiper JavaScript library thought the npm packages, but just for the purpose of the sample so it can quicky be run without addional setup. It is highly recomended to add the Swiper library as SPFx solution [external reference](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/basics/add-an-external-library) to resize the solution bundle size and improve Site Page load times.
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/drop-1.4-green.svg)
## Applies to
* [SharePoint Framework](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
## Prerequisites
- Office 365 subscription with SharePoint Online.
- SharePoint Framework [development environment](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) already set up.
## Solution
Solution|Author(s)
--------|---------
react-slide-swiper | Velin Georgiev ([@VelinGeorgiev](https://twitter.com/velingeorgiev))
## Version history
Version|Date|Comments
-------|----|--------
0.0.1|February 08, 2018 | Initial commit
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
---
## Minimal Path to Awesome
- Clone this repository.
- Open the command line, navigate to the web part folder and execute:
- `npm i`
- `gulp test` (optional)
- `gulp serve`
## Features
This Web Part illustrates the following concepts on top of the SharePoint Framework:
- Using React for building SharePoint Framework client-side web parts.
- How external css can be included in module.css.
- How JavaScript library can be loaded by the help of requirejs in web part.
- Mobile Touch capabilities
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-slide-swiper" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

View File

@ -0,0 +1,18 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"react-slide-swiper-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/reactSlideSwiper/ReactSlideSwiperWebPart.js",
"manifest": "./src/webparts/reactSlideSwiper/ReactSlideSwiperWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"ReactSlideSwiperWebPartStrings": "lib/webparts/reactSlideSwiper/loc/{locale}.js"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,7 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-slide-swiper",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,12 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-slide-swiper-client-side-solution",
"id": "c7fdd51c-469e-432f-bdde-930294d3a133",
"version": "1.0.0.0",
"includeClientSideAssets": true
},
"paths": {
"zippedPackage": "solution/react-slide-swiper.sppkg"
}
}

View File

@ -0,0 +1,10 @@
{
"$schema": "https://dev.office.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,45 @@
{
"$schema": "https://dev.office.com/json-schemas/core-build/tslint.schema.json",
// Display errors as warnings
"displayAsWarning": true,
// The TSLint task may have been configured with several custom lint rules
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
// project). If true, this flag will deactivate any of these rules.
"removeExistingRules": true,
// When true, the TSLint task is configured with some default TSLint "rules.":
"useDefaultConfigAsBase": false,
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
// which are active, other than the list of rules below.
"lintConfig": {
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-case": true,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"valid-typeof": true,
"variable-name": false,
"whitespace": false
}
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

27
samples/react-slide-swiper/gulpfile.js vendored Normal file
View File

@ -0,0 +1,27 @@
'use strict';
const gulp = require('gulp');
const path = require('path');
const build = require('@microsoft/sp-build-web');
const bundleAnalyzer = require('webpack-bundle-analyzer');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
build.configureWebpack.mergeConfig({
additionalConfiguration: (generatedConfiguration) => {
const lastDirName = path.basename(__dirname);
const dropPath = path.join(__dirname, 'temp', 'stats');
generatedConfiguration.plugins.push(new bundleAnalyzer.BundleAnalyzerPlugin({
openAnalyzer: false,
analyzerMode: 'static',
reportFilename: path.join(dropPath, `${lastDirName}.stats.html`),
generateStatsFile: true,
statsFilename: path.join(dropPath, `${lastDirName}.stats.json`),
logLevel: 'error'
}));
return generatedConfiguration;
}
});
build.initialize(gulp);

View File

@ -0,0 +1,35 @@
{
"name": "react-slide-swiper",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"react": "15.6.2",
"react-dom": "15.6.2",
"@types/react": "15.6.6",
"@types/react-dom": "15.5.6",
"@microsoft/sp-core-library": "~1.4.0",
"@microsoft/sp-webpart-base": "~1.4.0",
"@microsoft/sp-lodash-subset": "~1.4.0",
"@microsoft/sp-office-ui-fabric-core": "~1.4.0",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"swiper": "4.1.0"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.4.0",
"@microsoft/sp-module-interfaces": "~1.4.0",
"@microsoft/sp-webpart-workbench": "~1.4.0",
"gulp": "~3.9.1",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0",
"ajv": "~5.2.2",
"webpack-bundle-analyzer": "2.9.2"
}
}

View File

@ -0,0 +1,19 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "6d0ab1aa-db7a-4e80-a2b5-c83fd820f165",
"alias": "ReactSlideSwiperWebPart",
"componentType": "WebPart",
"version": "*",
"manifestVersion": 2,
"requiresCustomScript": false,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": { "default": "Other" },
"title": { "default": "ReactSlideSwiper" },
"description": { "default": "Sample that shows swipe slides experience" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "ReactSlideSwiper"
}
}]
}

View File

@ -0,0 +1,118 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneToggle,
} from '@microsoft/sp-webpart-base';
import * as strings from 'ReactSlideSwiperWebPartStrings';
import ReactSlideSwiper from './components/ReactSlideSwiper';
import { IReactSlideSwiperProps } from './components/IReactSlideSwiperProps';
import { IListServce } from './services/IListService';
import { ListMock } from './services/ListMock';
export interface IReactSlideSwiperWebPartProps {
enableNavigation: boolean;
enablePagination: boolean;
enableAutoplay: boolean;
delayAutoplay: number;
disableAutoplayOnInteraction: boolean;
slidesPerView: string;
slidesPerGroup: string;
spaceBetweenSlides: string;
enableGrabCursor: boolean;
enableLoop: boolean;
}
export default class ReactSlideSwiperWebPart extends BaseClientSideWebPart<IReactSlideSwiperWebPartProps> {
public render(): void {
const element: React.ReactElement<IReactSlideSwiperProps> = React.createElement(
ReactSlideSwiper,
{
listService: new ListMock(),
swiperOptions: this.properties
}
);
ReactDom.render(element, this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: 'Swiper Options'
},
displayGroupsAsAccordion: true,
groups: [
{
groupName: strings.GeneralGroupName,
groupFields: [
PropertyPaneToggle('enableNavigation', {
label: strings.EnableNavigation
}),
PropertyPaneToggle('enablePagination', {
label: strings.EnablePagination,
checked: true
}),
PropertyPaneTextField('slidesPerView', {
label: strings.SlidesPerWiew,
value: '3'
})
]
},
{
groupName: strings.AutoplayGroupName,
groupFields: [
PropertyPaneToggle('enableAutoplay', {
label: strings.EnableAutoplay
}),
PropertyPaneTextField('delayAutoplay', {
label: strings.DelayAutoplay,
description: strings.Miliseconds,
value: '2500',
disabled: !this.properties.enableAutoplay
}),
PropertyPaneToggle('disableAutoplayOnInteraction', {
label: strings.DisableAutoplayOnInteraction,
disabled: !this.properties.enableAutoplay
})
],
isCollapsed: true
},
{
groupName: strings.AdvancedGroupName,
groupFields: [
PropertyPaneTextField('slidesPerGroup', {
label: strings.SlidesPerGroup,
value: '3'
}),
PropertyPaneTextField('spaceBetweenSlides', {
label: strings.SpaceBetweenSlides,
description: strings.InPixels,
value: '5'
}),
PropertyPaneToggle('enableGrabCursor', {
label: strings.EnableGrabCursor
}),
PropertyPaneToggle('enableLoop', {
label: strings.EnableLoop
})
],
isCollapsed: true
}
]
}
]
};
}
}

View File

@ -0,0 +1,26 @@
//@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.card {
.wrapper{
border: 1px solid grey;
max-width: 300px;
height: 500px;
}
.url {
text-decoration: none;
}
.image {
height: 200px;
}
.title {
height: 120px;
}
.description {
height: 200px;
}
}

View File

@ -0,0 +1,20 @@
import * as React from 'react';
import { ICardProps } from './ICardProps';
import styles from './Card.module.scss';
export default class Card extends React.Component<ICardProps, {}> {
public render(): React.ReactElement<ICardProps> {
return (
<div className={styles.card}>
<div className={styles.wrapper}>
<img src={this.props.listItem.imageUrl} className={styles.image} />
<a href="#" className={styles.url} >
<h3 className={styles.title}>{this.props.listItem.title}</h3>
</a>
<p className={styles.description}>{this.props.listItem.description}</p>
</div>
</div>
);
}
}

View File

@ -0,0 +1,6 @@
import { ListItem } from "../../services/ListItem";
export interface ICardProps {
key: number;
listItem: ListItem;
}

View File

@ -0,0 +1,7 @@
import { IListServce } from "../services/IListService";
import { IReactSlideSwiperWebPartProps } from "../ReactSlideSwiperWebPart";
export interface IReactSlideSwiperProps {
listService: IListServce;
swiperOptions: IReactSlideSwiperWebPartProps;
}

View File

@ -0,0 +1,5 @@
import { ListItem } from "../services/ListItem";
export interface IReactSlideSwiperState {
listItems: Array<ListItem>;
}

View File

@ -0,0 +1,29 @@
//@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
@import '~swiper/dist/css/swiper.min.css';
.reactSlideSwiper {
.container {
width: 100%;
height: 100%;
}
.slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
}
}

View File

@ -0,0 +1,119 @@
import * as React from 'react';
import styles from './ReactSlideSwiper.module.scss';
import { IReactSlideSwiperProps } from './IReactSlideSwiperProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { IReactSlideSwiperState } from './IReactSlideSwiperState';
import { ListItem } from '../services/ListItem';
import Card from './Card/Card';
const Swiper = require('swiper/dist/js/swiper.min');
export default class ReactSlideSwiper extends React.Component<IReactSlideSwiperProps, IReactSlideSwiperState> {
constructor(props: IReactSlideSwiperProps) {
super(props);
this.state = { listItems: [] };
}
public componentDidMount(): void {
this.props.listService.getAll().then((result: Array<ListItem>) => {
// List items returned from the ListMock so we can
// change the state and display them.
this.setState({ listItems: result });
// Since we have list items rendered
// we can call the swiper and let it
// handle the swipe effect for the items.
this.setSwiper();
});
}
public render(): React.ReactElement<IReactSlideSwiperProps> {
return (
<div className={styles.reactSlideSwiper}>
<div className={`swiper-container ${styles.container}`}>
<div className='swiper-wrapper'>
{this.state.listItems.length &&
this.state.listItems.map((listItem, i) => {
return <div className={`swiper-slide ${styles.slide}`} key={i}>
<Card listItem={listItem} key={i} />
</div>;
})}
</div>
{this.props.swiperOptions.enableNavigation &&
<div className="swiper-button-next"></div>
}
{this.props.swiperOptions.enableNavigation &&
<div className="swiper-button-prev"></div>
}
{this.props.swiperOptions.enablePagination !== false &&
<div className="swiper-pagination"></div>
}
</div>
</div>
);
}
private setSwiper(): void {
const opts = this.props.swiperOptions;
const options: any = {
slidesPerView: parseInt(opts.slidesPerView) || 3,
slidesPerGroup: parseInt(opts.slidesPerGroup) || 3,
spaceBetween: parseInt(opts.spaceBetweenSlides) || 10,
loop: opts.enableLoop || false,
grabCursor: opts.enableGrabCursor || false,
breakpoints: {
1024: {
slidesPerView: 3,
spaceBetween: 10,
},
768: {
slidesPerView: 2,
spaceBetween: 10,
},
640: {
slidesPerView: 1,
spaceBetween: 5,
},
320: {
slidesPerView: 1,
spaceBetween: 5,
}
}
};
if (opts.enablePagination !== false) {
options.pagination = {
el: '.swiper-pagination',
clickable: true,
};
}
if (opts.enableNavigation) {
options.navigation = {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
};
}
if (opts.enableAutoplay) {
options.autoplay = {
delay: opts.delayAutoplay,
disableOnInteraction: opts.disableAutoplayOnInteraction,
};
}
return new Swiper('.swiper-container', options);
}
}

View File

@ -0,0 +1,20 @@
define([], function() {
return {
"SwiperOptions": "Swiper Options",
"GeneralGroupName": "General",
"EnableNavigation":"Enable navigation",
"EnablePagination":"Enable pagination",
"SlidesPerWiew": "Slides per view",
"AutoplayGroupName": "Autoplay",
"EnableAutoplay": "Enable autoplay",
"DelayAutoplay": "Delay autoplay",
"Miliseconds": "Miliseconds",
"DisableAutoplayOnInteraction": "Disable autoplay on interaction",
"AdvancedGroupName": "Advanced",
"SlidesPerGroup": "Slides per group",
"SpaceBetweenSlides": "Space between slides",
"InPixels": "In pixels",
"EnableGrabCursor": "Enable grab cursor",
"EnableLoop": "Enable loop",
}
});

View File

@ -0,0 +1,23 @@
declare interface IReactSlideSwiperWebPartStrings {
SwiperOptions: string;
GeneralGroupName: string;
EnableNavigation: string;
EnablePagination: string;
SlidesPerWiew: string;
AutoplayGroupName: string;
EnableAutoplay: string;
DelayAutoplay: string;
Miliseconds: string;
DisableAutoplayOnInteraction: string;
AdvancedGroupName: string;
SlidesPerGroup: string;
SpaceBetweenSlides: string;
InPixels: string;
EnableGrabCursor: string;
EnableLoop: string;
}
declare module 'ReactSlideSwiperWebPartStrings' {
const strings: IReactSlideSwiperWebPartStrings;
export = strings;
}

View File

@ -0,0 +1,5 @@
import { ListItem } from "./ListItem";
export interface IListServce {
getAll(): Promise<Array<ListItem>>;
}

View File

@ -0,0 +1,6 @@
export class ListItem {
public title: string;
public description:string;
public imageUrl: string;
}

View File

@ -0,0 +1,137 @@
import { ListItem } from "./ListItem";
import { IListServce } from "./IListService";
export class ListMock implements IListServce {
public getAll(): Promise<Array<ListItem>> {
return new Promise<Array<ListItem>>((resolve:any) => {
const fakeData: Array<ListItem> = [
{
title: 'A convergent value empowers the standard-setters',
description: 'The General Head of IT Strategy benchmarks business-for-business agilities',
imageUrl: 'https://blog.velingeorgiev.com/static/images/OFFICE365.png'
},
{
title: 'The Digital Marketers empower a digitized correlation',
description: 'Whereas synchronized brand values promote strategy formulations',
imageUrl: 'https://blog.velingeorgiev.com/static/images/POWERSHELL.png'
},
{
title: 'The market thinker strategically standardizes a competitive success',
description: 'The thinkers/planners benchmark a disciplined growth momentum',
imageUrl: 'https://blog.velingeorgiev.com/static/images/PYTHON.png'
},
{
title: 'We are going to secure our cross-pollinations',
description: 'We are working hard to reintermediate a competitive advantage, while the gatekeeper straightforwardly identifies barriers to success',
imageUrl: 'https://blog.velingeorgiev.com/static/images/SP.png'
},
{
title: 'A convergent value empowers the standard-setters',
description: 'The General Head of IT Strategy benchmarks business-for-business agilities',
imageUrl: 'https://blog.velingeorgiev.com/static/images/JAVASCRIPT.png'
},
{
title: 'The Digital Marketers empower a digitized correlation',
description: 'Whereas synchronized brand values promote strategy formulations',
imageUrl: 'https://blog.velingeorgiev.com/static/images/POWERSHELL.png'
},
{
title: 'The market thinker strategically standardizes a competitive success',
description: 'The thinkers/planners benchmark a disciplined growth momentum',
imageUrl: 'https://blog.velingeorgiev.com/static/images/PYTHON.png'
},
{
title: 'We are going to secure our cross-pollinations',
description: 'We are working hard to reintermediate a competitive advantage, while the gatekeeper straightforwardly identifies barriers to success',
imageUrl: 'https://blog.velingeorgiev.com/static/images/SP.png'
},
{
title: 'A convergent value empowers the standard-setters',
description: 'The General Head of IT Strategy benchmarks business-for-business agilities',
imageUrl: 'https://blog.velingeorgiev.com/static/images/OFFICE365.png'
},
{
title: 'The Digital Marketers empower a digitized correlation',
description: 'Whereas synchronized brand values promote strategy formulations',
imageUrl: 'https://blog.velingeorgiev.com/static/images/JAVASCRIPT.png'
},
{
title: 'The market thinker strategically standardizes a competitive success',
description: 'The thinkers/planners benchmark a disciplined growth momentum',
imageUrl: 'https://blog.velingeorgiev.com/static/images/PYTHON.png'
},
{
title: 'We are going to secure our cross-pollinations',
description: 'We are working hard to reintermediate a competitive advantage, while the gatekeeper straightforwardly identifies barriers to success',
imageUrl: 'https://blog.velingeorgiev.com/static/images/SP.png'
},
{
title: 'A convergent value empowers the standard-setters',
description: 'The General Head of IT Strategy benchmarks business-for-business agilities',
imageUrl: 'https://blog.velingeorgiev.com/static/images/OFFICE365.png'
},
{
title: 'The Digital Marketers empower a digitized correlation',
description: 'Whereas synchronized brand values promote strategy formulations',
imageUrl: 'https://blog.velingeorgiev.com/static/images/POWERSHELL.png'
},
{
title: 'The market thinker strategically standardizes a competitive success',
description: 'The thinkers/planners benchmark a disciplined growth momentum',
imageUrl: 'https://blog.velingeorgiev.com/static/images/JAVASCRIPT.png'
},
{
title: 'We are going to secure our cross-pollinations',
description: 'We are working hard to reintermediate a competitive advantage, while the gatekeeper straightforwardly identifies barriers to success',
imageUrl: 'https://blog.velingeorgiev.com/static/images/SP.png'
},
{
title: 'A convergent value empowers the standard-setters',
description: 'The General Head of IT Strategy benchmarks business-for-business agilities',
imageUrl: 'https://blog.velingeorgiev.com/static/images/OFFICE365.png'
},
{
title: 'The Digital Marketers empower a digitized correlation',
description: 'Whereas synchronized brand values promote strategy formulations',
imageUrl: 'https://blog.velingeorgiev.com/static/images/POWERSHELL.png'
},
{
title: 'The market thinker strategically standardizes a competitive success',
description: 'The thinkers/planners benchmark a disciplined growth momentum',
imageUrl: 'https://blog.velingeorgiev.com/static/images/PYTHON.png'
},
{
title: 'We are going to secure our cross-pollinations',
description: 'We are working hard to reintermediate a competitive advantage, while the gatekeeper straightforwardly identifies barriers to success',
imageUrl: 'https://blog.velingeorgiev.com/static/images/JAVASCRIPT.png'
},
{
title: 'A convergent value empowers the standard-setters',
description: 'The General Head of IT Strategy benchmarks business-for-business agilities',
imageUrl: 'https://blog.velingeorgiev.com/static/images/OFFICE365.png'
},
{
title: 'The Digital Marketers empower a digitized correlation',
description: 'Whereas synchronized brand values promote strategy formulations',
imageUrl: 'https://blog.velingeorgiev.com/static/images/POWERSHELL.png'
},
{
title: 'The market thinker strategically standardizes a competitive success',
description: 'The thinkers/planners benchmark a disciplined growth momentum',
imageUrl: 'https://blog.velingeorgiev.com/static/images/PYTHON.png'
},
{
title: 'We are going to secure our cross-pollinations',
description: 'We are working hard to reintermediate a competitive advantage, while the gatekeeper straightforwardly identifies barriers to success',
imageUrl: 'https://blog.velingeorgiev.com/static/images/SP.png'
}
];
resolve(fakeData);
});
}
}

View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
]
}
}