Merge pull request #1241 from siddharth-vaghasia/react-js-css-ref-sample
Added new sample webpart which add js, css file references to modern pages.
This commit is contained in:
commit
59111889cb
|
@ -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
|
|
@ -0,0 +1,32 @@
|
|||
# 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
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"isCreatingSolution": false,
|
||||
"environment": "spo",
|
||||
"version": "1.9.1",
|
||||
"libraryName": "react-add-js-css-ref",
|
||||
"libraryId": "d9c30e1a-bf06-46fa-807d-ce5182d9c91c",
|
||||
"packageManager": "npm",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
## SPFx webpart to add JS and CSS reference on Modern Pages via SPFx application customizer extension
|
||||
|
||||
This repo is a react based SPFx web part and extension that allows users to add/modify/delete custom js and css file references using SPFx application customizer extension all modern pages within SP online site. This web part provides an interface to JS and CSS file references so that we don't have to modify code when we need to change references or add new references in the future. As part of security measures, this actions on web part can be only accessed by users who have Manage web permission on site.
|
||||
|
||||
WebPart in Action
|
||||
|
||||
![Webpart in action](assets/webpartinaction.gif?raw=true "Webpart in action")
|
||||
|
||||
Challanges/Drawback with ONLY using SPFx extension for adding js and css file references.
|
||||
* JS and CSS file refereces links needs to be hardcoded in solution
|
||||
* Changes to code required if we need to change add new reference or remove existing reference.
|
||||
* Redeployment of package and installation
|
||||
* Diffrent solution would be required for diffrent site collections as we would defintely need diffrent header js and css file refereces for each site collection(most of case)
|
||||
* High maintananence and time consuming for simple task.
|
||||
|
||||
To overcome this drawbacks, this solution comes handy. This is resuable component which can be used by developers to eliminate creating Extension on thier own. Feel free to connect on twitter:@siddh_me for any details.
|
||||
|
||||
### Features of solution
|
||||
|
||||
* WebPart to configure JS and CSS file reference.
|
||||
* Edit functionality if atleast one JS or CSS reference is already added via this solution
|
||||
* Completely remove all the references added via this solution
|
||||
* Support for relative url also, if your js and css file is refered from some document library in same site collection.
|
||||
Path can be '/sites/mysc/style library/js/custom.js' or '/sites/mysc/style library/css/custom.css'
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||
![drop](https://img.shields.io/badge/version-1.9.1-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
|
||||
* [Office 365 tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
|
||||
|
||||
### Package and Deploy
|
||||
|
||||
Note - If you don't want to build and package on your own, you can directly download package at this [location](./sharepoint/solutions/react-add-js-css-ref.sppkg) and upload to app catalog and install app on required site collection. Skip below steps and directly go to How to use section.
|
||||
Clone the solution and make sure there is no error before packaging. Try first on local work bench.
|
||||
|
||||
Change the pageURL property in /config/serve.json - This should be a valid modern page on your site collection.
|
||||
|
||||
```bash
|
||||
git clone the repo
|
||||
npm i
|
||||
gulp serve
|
||||
```
|
||||
- Execute the following gulp task to bundle your solution. This executes a release build of your project by using a dynamic label as the host URL for your assets. This URL is automatically updated based on your tenant CDN settings:
|
||||
```bash
|
||||
gulp bundle --ship
|
||||
```
|
||||
- Execute the following task to package your solution. This creates an updated webpart.sppkg package on the sharepoint/solution folder.
|
||||
```bash
|
||||
gulp package-solution --ship
|
||||
```
|
||||
- Upload or drag and drop the newly created client-side solution package to the app catalog in your tenant.
|
||||
- Based on your tenant settings, if you would not have CDN enabled in your tenant, and the includeClientSideAssets setting would be true in the package-solution.json, the loading URL for the assets would be dynamically updated and pointing directly to the ClientSideAssets folder located in the app catalog site collection.
|
||||
|
||||
|
||||
### How to Use Solution
|
||||
* Once app is deployed to app catalog sucessfully.
|
||||
* Install app to required site collection
|
||||
* Create new modern page. Add 'addJsCssReference' webpart to page. Publish the page.
|
||||
* Use grid to add js and css file references, both are seperate sections.
|
||||
* On Success message - Refresh the page and you would see your js and css files will be loaded.
|
||||
* To Edit/Remove, go to same page again and Use 'Activate' or 'Deactivate'.
|
||||
* Only Users with Manage Web permission will be able to access webpart and add/modify references.
|
||||
|
||||
### High level design of Solution
|
||||
|
||||
* SPFx solution with 2 components 1. SPFx Webpart 2. SPFx Extension Application Customizer
|
||||
* Disables Automatic activation of SPFx extension when app is installed.
|
||||
* React based solution
|
||||
* Register Custom action with ClientSideComponentId of Extension component
|
||||
* Passes parameters to Extension with ClientSideComponentProperties
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-add-js-css-ref | [Siddharth Vaghasia](https://www.linkedin.com/in/siddharthvaghasia/)
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.0|Apr 24, 2020|Initial release
|
||||
|
||||
## 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.**
|
||||
|
||||
For any issue or help, Buzz me on twitter:([siddh_me](https://twitter.com/siddh_me/))
|
||||
|
||||
> Sharing is caring!
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-add-js-css-ref" />
|
Binary file not shown.
After Width: | Height: | Size: 4.2 MiB |
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"reference-injector-application-customizer": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/extensions/referenceInjector/ReferenceInjectorApplicationCustomizer.js",
|
||||
"manifest": "./src/extensions/referenceInjector/ReferenceInjectorApplicationCustomizer.manifest.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"add-js-css-reference-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/addJsCssReference/AddJsCssReferenceWebPart.js",
|
||||
"manifest": "./src/webparts/addJsCssReference/AddJsCssReferenceWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"ReferenceInjectorApplicationCustomizerStrings": "lib/extensions/referenceInjector/loc/{locale}.js",
|
||||
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js",
|
||||
"AddJsCssReferenceWebPartStrings": "lib/webparts/addJsCssReference/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-add-js-css-ref",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-add-js-css-ref-client-side-solution",
|
||||
"id": "d9c30e1a-bf06-46fa-807d-ce5182d9c91c",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false,
|
||||
"features": [
|
||||
{
|
||||
"title": "Application Extension - Deployment of custom action.",
|
||||
"description": "Deploys a custom action with ClientSideComponentId association",
|
||||
"id": "201c7969-a60d-492a-83d2-db3088515c51",
|
||||
"version": "1.0.0.0",
|
||||
"assets": {
|
||||
"elementManifests": [
|
||||
"elements.xml",
|
||||
"clientsideinstance.xml"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-add-js-css-ref.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||
"port": 4321,
|
||||
"https": true,
|
||||
"serveConfigurations": {
|
||||
"default": {
|
||||
"pageUrl": "https://contoso.sharepoint.com/sites/mysc/_layouts/15/viewlsts.aspx",
|
||||
"customActions": {
|
||||
"38afa8d7-b498-4529-9f99-6279392f9309": {
|
||||
"location": "ClientSideExtension.ApplicationCustomizer",
|
||||
"properties": {
|
||||
"testMessage": "Test message"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"referenceInjector": {
|
||||
"pageUrl": "https://contoso.sharepoint.com/sites/mysc/_layouts/15/viewlsts.aspx",
|
||||
"customActions": {
|
||||
"38afa8d7-b498-4529-9f99-6279392f9309": {
|
||||
"location": "ClientSideExtension.ApplicationCustomizer",
|
||||
"properties": {
|
||||
"testMessage": "Test message"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"initialPage": "https://localhost:5432/workbench",
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
|
||||
build.initialize(gulp);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"name": "react-add-js-css-ref",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/decorators": "1.9.1",
|
||||
"@microsoft/sp-application-base": "1.9.1",
|
||||
"@microsoft/sp-core-library": "1.9.1",
|
||||
"@microsoft/sp-dialog": "1.9.1",
|
||||
"@microsoft/sp-lodash-subset": "1.9.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.9.1",
|
||||
"@microsoft/sp-webpart-base": "1.9.1",
|
||||
"@pnp/sp": "^2.0.3",
|
||||
"@pnp/spfx-controls-react": "1.16.0",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/react": "16.8.8",
|
||||
"@types/react-dom": "16.8.3",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"office-ui-fabric-react": "6.189.2",
|
||||
"react": "16.8.5",
|
||||
"react-dom": "16.8.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "1.9.1",
|
||||
"@microsoft/sp-tslint-rules": "1.9.1",
|
||||
"@microsoft/sp-module-interfaces": "1.9.1",
|
||||
"@microsoft/sp-webpart-workbench": "1.9.1",
|
||||
"@microsoft/rush-stack-compiler-2.9": "0.7.16",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"ajv": "~5.2.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.8.8"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-extension-manifest.schema.json",
|
||||
|
||||
"id": "38afa8d7-b498-4529-9f99-6279392f9309",
|
||||
"alias": "ReferenceInjectorApplicationCustomizer",
|
||||
"componentType": "Extension",
|
||||
"extensionType": "ApplicationCustomizer",
|
||||
|
||||
// The "*" signifies that the version should be taken from the package.json
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
|
||||
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||
"requiresCustomScript": false
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import { override } from '@microsoft/decorators';
|
||||
import { Log } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseApplicationCustomizer
|
||||
} from '@microsoft/sp-application-base';
|
||||
import { Dialog } from '@microsoft/sp-dialog';
|
||||
|
||||
import * as strings from 'ReferenceInjectorApplicationCustomizerStrings';
|
||||
|
||||
const LOG_SOURCE: string = 'ReferenceInjectorApplicationCustomizer';
|
||||
|
||||
/**
|
||||
* If your command set uses the ClientSideComponentProperties JSON input,
|
||||
* it will be deserialized into the BaseExtension.properties object.
|
||||
* You can define an interface to describe it.
|
||||
*/
|
||||
export interface IReferenceInjectorApplicationCustomizerProperties {
|
||||
// This is an example; replace with your own property
|
||||
jsfiles:any[];
|
||||
cssfiles:any[];
|
||||
}
|
||||
|
||||
/** A Custom Action which can be run during execution of a Client Side Application */
|
||||
export default class ReferenceInjectorApplicationCustomizer
|
||||
extends BaseApplicationCustomizer<IReferenceInjectorApplicationCustomizerProperties> {
|
||||
|
||||
@override
|
||||
public onInit(): Promise<void> {
|
||||
Log.info(LOG_SOURCE, `Initialized ${strings.Title}`);
|
||||
|
||||
if(this.properties.jsfiles)
|
||||
{
|
||||
this.properties.jsfiles.forEach(element => {
|
||||
let JsTag: HTMLScriptElement = document.createElement("script");
|
||||
JsTag.src = element.FilePath;
|
||||
|
||||
JsTag.type = "text/javascript";
|
||||
document.body.appendChild(JsTag);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if(this.properties.cssfiles){
|
||||
|
||||
this.properties.cssfiles.forEach(element => {
|
||||
let cssLink: HTMLLinkElement = document.createElement("link");
|
||||
cssLink.href = element.FilePath;
|
||||
cssLink.type = "text/css";
|
||||
cssLink.rel = "stylesheet";
|
||||
document.body.appendChild(cssLink);
|
||||
});
|
||||
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"Title": "ReferenceInjectorApplicationCustomizer"
|
||||
}
|
||||
});
|
8
samples/react-add-js-css-ref/src/extensions/referenceInjector/loc/myStrings.d.ts
vendored
Normal file
8
samples/react-add-js-css-ref/src/extensions/referenceInjector/loc/myStrings.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
declare interface IReferenceInjectorApplicationCustomizerStrings {
|
||||
Title: string;
|
||||
}
|
||||
|
||||
declare module 'ReferenceInjectorApplicationCustomizerStrings' {
|
||||
const strings: IReferenceInjectorApplicationCustomizerStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "9fa13c84-2736-4413-be49-e163b796b143",
|
||||
"alias": "AddJsCssReferenceWebPart",
|
||||
"componentType": "WebPart",
|
||||
|
||||
// The "*" signifies that the version should be taken from the package.json
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
|
||||
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||
"requiresCustomScript": false,
|
||||
"supportedHosts": ["SharePointWebPart"],
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "AddJsCssReference" },
|
||||
"description": { "default": "AddJsCssReference description" },
|
||||
"officeFabricIconFontName": "Page",
|
||||
"properties": {
|
||||
"description": "AddJsCssReference"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'AddJsCssReferenceWebPartStrings';
|
||||
import AddJsCssReference from './components/AddJsCssReference';
|
||||
import { IAddJsCssReferenceProps } from './components/IAddJsCssReferenceProps';
|
||||
|
||||
export interface IAddJsCssReferenceWebPartProps {
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default class AddJsCssReferenceWebPart extends BaseClientSideWebPart<IAddJsCssReferenceWebPartProps> {
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<IAddJsCssReferenceProps > = React.createElement(
|
||||
AddJsCssReference,
|
||||
{
|
||||
description: this.properties.description,
|
||||
context:this.context
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('description', {
|
||||
label: strings.DescriptionFieldLabel
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.addJsCssReference {
|
||||
|
||||
.container {
|
||||
// max-width: 700px;
|
||||
margin: 0px auto;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
@include ms-fontColor-black;
|
||||
// background-color: $ms-color-themeDark;
|
||||
// padding: 20px;
|
||||
}
|
||||
|
||||
.column {
|
||||
// @include ms-Grid-col;
|
||||
@include ms-lg12;
|
||||
// @include ms-xl8;
|
||||
// @include ms-xlPush2;
|
||||
// @include ms-lgPush1;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
@include ms-fontColor-black;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-black;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-black;
|
||||
}
|
||||
|
||||
.button {
|
||||
// Our button
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
|
||||
// Primary Button
|
||||
min-width: 80px;
|
||||
background-color: $ms-color-themePrimary;
|
||||
border-color: $ms-color-themePrimary;
|
||||
color: $ms-color-white;
|
||||
|
||||
// Basic Button
|
||||
outline: transparent;
|
||||
position: relative;
|
||||
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: $ms-font-size-m;
|
||||
font-weight: $ms-font-weight-regular;
|
||||
border-width: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
|
||||
.label {
|
||||
font-weight: $ms-font-weight-semibold;
|
||||
font-size: $ms-font-size-m;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 4px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.customicons{
|
||||
font-size: 14px;
|
||||
color:$ms-color-themePrimary;
|
||||
}
|
||||
|
||||
.filepath{
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ms-DetailsList{
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,491 @@
|
|||
import * as React from 'react';
|
||||
import styles from './AddJsCssReference.module.scss';
|
||||
import { IAddJsCssReferenceProps } from './IAddJsCssReferenceProps';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import { TextField, MaskedTextField } from 'office-ui-fabric-react/lib/TextField';
|
||||
import { ListView, IViewField, SelectionMode} from "@pnp/spfx-controls-react/lib/ListView";
|
||||
import {MessageBarType,Link,Separator, CommandBarButton,IStackStyles,Text,MessageBar,PrimaryButton,DefaultButton,Dialog,DialogFooter,DialogType,Stack, IStackTokens, updateA } from 'office-ui-fabric-react';
|
||||
import { sp} from "@pnp/sp";
|
||||
import "@pnp/sp/webs";
|
||||
import "@pnp/sp/user-custom-actions";
|
||||
import "@pnp/sp/presets/all";
|
||||
import {TypedHash} from "@pnp/common";
|
||||
import { IUserCustomActionAddResult,IUserCustomActionUpdateResult,IUserCustomAction } from '@pnp/sp/user-custom-actions';
|
||||
import { createTheme, ITheme } from 'office-ui-fabric-react/lib/Styling';
|
||||
import { mergeStyleSets } from 'office-ui-fabric-react/lib/Styling';
|
||||
import { PermissionKind } from '@pnp/sp/presets/all';
|
||||
|
||||
|
||||
const stackTokens: IStackTokens = { childrenGap: 40 };
|
||||
const CustomActionTitle = 'JSCssAppCustomizer';
|
||||
const ApplicationCustomizerComponentID = '38afa8d7-b498-4529-9f99-6279392f9309';
|
||||
const description = 'This user action is of type application customizer to custom js and css file references via SFPx extension';
|
||||
const theme: ITheme = createTheme({
|
||||
fonts: {
|
||||
medium: {
|
||||
// fontFamily: 'Monaco, Menlo, Consolas',
|
||||
fontSize: '18px'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const stackStyles: Partial<IStackStyles> = { root: { height: 30 } };
|
||||
|
||||
export interface IAddJsCssReferenceState {
|
||||
disableRegisterButton: boolean;
|
||||
disableRemoveButton: boolean;
|
||||
jsfiles:any[];
|
||||
cssfiles:any[];
|
||||
currentjsRef:string;
|
||||
currentcssRef:string;
|
||||
hideJSDailog:boolean;
|
||||
hideCSSDailog:boolean;
|
||||
currentCustomAction:any;
|
||||
isEdit:boolean;
|
||||
editIndex:number;
|
||||
showMesssage:boolean;
|
||||
successmessage:string;
|
||||
userHasPermission:boolean;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default class AddJsCssReference extends React.Component<IAddJsCssReferenceProps, IAddJsCssReferenceState> {
|
||||
|
||||
|
||||
private viewFields: any[] = [
|
||||
{
|
||||
name: "Type",
|
||||
displayName: "Action",
|
||||
minWidth: 60,
|
||||
maxWidth:60,
|
||||
render: (item,index) =>{
|
||||
console.log(item);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stack horizontal tokens={{childrenGap:20}}>
|
||||
<i className={"ms-Icon ms-Icon--Edit " + styles.customicons} onClick={()=> this.editClicked(item,index)} aria-hidden="true"></i>
|
||||
<i className={"ms-Icon ms-Icon--Delete " + styles.customicons} onClick={()=> this.deleteClicked(item,index)} aria-hidden="true"></i>
|
||||
</Stack>
|
||||
</React.Fragment>
|
||||
);
|
||||
},
|
||||
className:"test"
|
||||
},
|
||||
{
|
||||
name: "FilePath",
|
||||
displayName: "FilePath",
|
||||
minWidth:600,
|
||||
render: (item,index) =>{
|
||||
console.log(item);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<span className={styles.filepath}>
|
||||
{item.FilePath}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
// maxWidth:800
|
||||
}
|
||||
|
||||
|
||||
];
|
||||
|
||||
|
||||
constructor(props: IAddJsCssReferenceProps,state:IAddJsCssReferenceProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
disableRegisterButton:false,
|
||||
disableRemoveButton:false,
|
||||
jsfiles:[],
|
||||
cssfiles:[],
|
||||
currentjsRef:"",
|
||||
currentcssRef:"",
|
||||
hideJSDailog:true,
|
||||
hideCSSDailog:true,
|
||||
currentCustomAction:null,
|
||||
isEdit:false,
|
||||
editIndex:-1,
|
||||
showMesssage:false,
|
||||
successmessage:"",
|
||||
userHasPermission:false
|
||||
|
||||
|
||||
};
|
||||
|
||||
sp.setup(this.props.context);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public render(): React.ReactElement<IAddJsCssReferenceProps> {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
|
||||
{this.state.userHasPermission &&
|
||||
<div className={styles.addJsCssReference}>
|
||||
<div className={ styles.container }>
|
||||
<div className={ styles.row }>
|
||||
<div className={ styles.column }>
|
||||
<span className={ styles.title }>SPFx JS CSS References WebPart</span>
|
||||
<p className={ styles.subTitle }>This webpart can be used to add reference to custom js files and css files via SPFx extension application customizer.</p>
|
||||
|
||||
</div>
|
||||
<div className={ styles.row }>
|
||||
<div className={ styles.column }>
|
||||
{this.state.showMesssage &&
|
||||
<MessageBar dismissButtonAriaLabel="Close" onDismiss={()=>{ this.setState({showMesssage:false});}} messageBarType={MessageBarType.success}>
|
||||
{this.state.successmessage}
|
||||
</MessageBar>
|
||||
}
|
||||
|
||||
{this.state.currentCustomAction && this.state.showMesssage != true &&
|
||||
<MessageBar >
|
||||
We found you already have some custom js and css files references added via this customizer. Feel free to Edit or Remove references.
|
||||
</MessageBar>
|
||||
}
|
||||
|
||||
|
||||
<div id="jsfiles">
|
||||
<Separator></Separator>
|
||||
<Stack horizontal styles={stackStyles} tokens={stackTokens}>
|
||||
<Text theme={theme}>Javascript Files</Text>
|
||||
<CommandBarButton iconProps={{iconName: 'Add'}} text="Add JS Link" onClick={()=>this.openAddJSDailog()} />
|
||||
</Stack>
|
||||
<Separator></Separator>
|
||||
{/* <PrimaryButton text="Add New Item" } /> */}
|
||||
|
||||
{this.state.jsfiles.length === 0 &&
|
||||
<React.Fragment>
|
||||
<MessageBar>
|
||||
No References Found.
|
||||
<Link href="#" onClick={()=>this.openAddJSDailog()}>
|
||||
Click here
|
||||
</Link>
|
||||
<Text> to add new.</Text>
|
||||
</MessageBar>
|
||||
<br/>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
{this.state.jsfiles.length >0 &&
|
||||
<ListView
|
||||
items={this.state.jsfiles}
|
||||
viewFields={this.viewFields}
|
||||
/>
|
||||
}
|
||||
<Dialog
|
||||
minWidth={600}
|
||||
maxWidth={900}
|
||||
hidden={this.state.hideJSDailog}
|
||||
onDismiss={this._closeJSDialog}
|
||||
dialogContentProps={{
|
||||
type: DialogType.normal,
|
||||
title: 'Add JS Reference',
|
||||
// subText: 'Enter a valid JS file link.'
|
||||
}}
|
||||
modalProps={{
|
||||
isBlocking: false,
|
||||
// styles: { main: { maxWidth: 450 } }
|
||||
}}
|
||||
>
|
||||
|
||||
<TextField required onChange={evt => this.updateJSValue(evt)} value={this.state.currentjsRef} label="URL" resizable={false} />
|
||||
<DialogFooter>
|
||||
<PrimaryButton onClick={()=>this._addJsReference()} text="Add" />
|
||||
<DefaultButton onClick={this._closeJSDialog} text="Cancel" />
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
</div>
|
||||
<div id="cssfiles">
|
||||
<br/>
|
||||
<Stack horizontal styles={stackStyles} tokens={stackTokens}>
|
||||
<Text theme={theme}>CSS Files</Text>
|
||||
<CommandBarButton iconProps={{iconName: 'Add'}} text="Add CSS Link" onClick={()=>this.openAddCSSDailog()} />
|
||||
</Stack>
|
||||
{/* <PrimaryButton text="Add New Item" onClick={()=>this.openAddCSSDailog()} /> */}
|
||||
<Separator></Separator>
|
||||
{this.state.cssfiles.length === 0 &&
|
||||
<React.Fragment>
|
||||
<MessageBar>
|
||||
No References Found.
|
||||
<Link href="#" onClick={()=>this.openAddCSSDailog()}>
|
||||
Click here
|
||||
</Link>
|
||||
<Text> to add new.</Text>
|
||||
</MessageBar>
|
||||
<br/>
|
||||
</React.Fragment>
|
||||
}
|
||||
{this.state.cssfiles.length > 0 &&
|
||||
|
||||
<ListView
|
||||
items={this.state.cssfiles}
|
||||
viewFields={this.viewFields}
|
||||
// iconFieldName="ServerRelativeUrl"
|
||||
|
||||
// selectionMode={SelectionMode.multiple}
|
||||
// selection={this._getSelection}
|
||||
|
||||
/>
|
||||
}
|
||||
<Dialog
|
||||
minWidth={600}
|
||||
maxWidth={900}
|
||||
hidden={this.state.hideCSSDailog}
|
||||
onDismiss={this._closeCSSDialog}
|
||||
dialogContentProps={{
|
||||
type: DialogType.normal,
|
||||
title: 'Add CSS Reference',
|
||||
// subText: 'Enter a valid CSS file link.'
|
||||
}}
|
||||
modalProps={{
|
||||
isBlocking: false,
|
||||
// styles: { main: { minWidth: 500 !important ,width:500} }
|
||||
}}
|
||||
>
|
||||
|
||||
<TextField required onChange={evt => this.updateCSSValue(evt)} value={this.state.currentcssRef} label="URL" />
|
||||
<DialogFooter>
|
||||
<PrimaryButton onClick={()=>this._addCSSReference()} text="Add" />
|
||||
<DefaultButton onClick={this._closeCSSDialog} text="Cancel" />
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<Stack horizontal tokens={stackTokens}>
|
||||
<PrimaryButton text="Activate" onClick={()=>this._registerClicked()} disabled={(this.state.jsfiles.length>0 || this.state.cssfiles.length>0 )?false:true} />
|
||||
<DefaultButton text="Deactivate" onClick={()=> this._removeClicked()} disabled={this.state.currentCustomAction==null?true:false} />
|
||||
</Stack>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
<MessageBar messageBarType={MessageBarType.severeWarning}>
|
||||
Access denied, you do not have permission to access this section. Please connect with your site admins.
|
||||
</MessageBar>
|
||||
}
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
|
||||
this.checkPermisson();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async checkPermisson(){
|
||||
const perms2 = await sp.web.currentUserHasPermissions(PermissionKind.ManageWeb);
|
||||
console.log(perms2);
|
||||
var temp = false;
|
||||
|
||||
if(temp){
|
||||
this.getCustomAction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private _registerClicked(): void {
|
||||
this.setCustomAction();
|
||||
}
|
||||
|
||||
private _removeClicked(): void {
|
||||
const uca = sp.web.userCustomActions.getById(this.state.currentCustomAction.Id);
|
||||
const response = uca.delete();
|
||||
console.log("removed custom action");
|
||||
console.log(response);
|
||||
this.setState({currentCustomAction:null,jsfiles:[],cssfiles:[],
|
||||
showMesssage:true,successmessage:"Application Customizer deactivated sucessfully."});
|
||||
}
|
||||
|
||||
private updateJSValue(evt) {
|
||||
this.setState({
|
||||
currentjsRef: evt.target.value
|
||||
});
|
||||
}
|
||||
|
||||
private updateCSSValue(evt) {
|
||||
this.setState({
|
||||
currentcssRef: evt.target.value
|
||||
});
|
||||
}
|
||||
|
||||
private editClicked (item,index){
|
||||
|
||||
if(item.Type == "js") {
|
||||
this.setState({hideJSDailog:false,
|
||||
currentjsRef:item.FilePath,
|
||||
isEdit:true,
|
||||
editIndex:index
|
||||
});
|
||||
}
|
||||
|
||||
if(item.Type == "css") {
|
||||
this.setState({hideCSSDailog:false,
|
||||
currentcssRef:item.FilePath,
|
||||
isEdit:true,
|
||||
editIndex:index
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private deleteClicked (item,index){
|
||||
|
||||
if(item.Type == "css") {
|
||||
|
||||
let currentitems = this.state.cssfiles.map((x) => x);
|
||||
currentitems.splice(index,1);
|
||||
this.setState({cssfiles:currentitems});
|
||||
}
|
||||
else if(item.Type == "js"){
|
||||
let currentitems = this.state.jsfiles.map((x) => x);
|
||||
currentitems.splice(index,1);
|
||||
this.setState({jsfiles:currentitems});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private openAddJSDailog (){
|
||||
this.setState({hideJSDailog:false});
|
||||
}
|
||||
|
||||
private openAddCSSDailog (){
|
||||
this.setState({hideCSSDailog:false});
|
||||
}
|
||||
|
||||
private _addJsReference (){
|
||||
if(!this.state.isEdit){
|
||||
var item = {
|
||||
FilePath:this.state.currentjsRef,
|
||||
Type: "js"
|
||||
};
|
||||
|
||||
let currentitems = this.state.jsfiles.map((x) => x);
|
||||
currentitems.push(item);
|
||||
currentitems[this.state.jsfiles.length] = item;
|
||||
this.setState({jsfiles:currentitems,
|
||||
hideJSDailog:true,currentjsRef:""});
|
||||
}
|
||||
else{
|
||||
item = {
|
||||
FilePath:this.state.currentjsRef,
|
||||
Type: "js"
|
||||
};
|
||||
let currentitems = this.state.jsfiles.map((x) => x);
|
||||
currentitems[this.state.editIndex] = item;
|
||||
this.setState({jsfiles:currentitems,
|
||||
hideJSDailog:true,currentjsRef:"",
|
||||
isEdit:false,editIndex:-1});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private _addCSSReference (){
|
||||
if(!this.state.isEdit){
|
||||
console.log("add item to grid");
|
||||
var item = {
|
||||
FilePath:this.state.currentcssRef,
|
||||
Type:"css"
|
||||
};
|
||||
|
||||
let currentitems = this.state.cssfiles.map((x) => x);
|
||||
currentitems.push(item);
|
||||
currentitems[this.state.cssfiles.length] = item;
|
||||
this.setState({cssfiles:currentitems,
|
||||
hideCSSDailog:true,
|
||||
currentcssRef:""});
|
||||
}
|
||||
else{
|
||||
console.log("add item to grid");
|
||||
item = {
|
||||
FilePath:this.state.currentcssRef,
|
||||
Type:"css"
|
||||
};
|
||||
|
||||
let currentitems = this.state.cssfiles.map((x) => x);
|
||||
|
||||
currentitems[this.state.editIndex] = item;
|
||||
this.setState({cssfiles:currentitems,
|
||||
hideCSSDailog:true,
|
||||
currentcssRef:"",
|
||||
editIndex:-1,isEdit:false});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
private _closeJSDialog = (): void => {
|
||||
this.setState({ hideJSDailog: true });
|
||||
}
|
||||
private _closeCSSDialog = (): void => {
|
||||
this.setState({ hideCSSDailog: true });
|
||||
}
|
||||
|
||||
private async getCustomAction(){
|
||||
|
||||
var web = await sp.web.get();
|
||||
console.log(web);
|
||||
var customactions:any = await sp.web.userCustomActions.get();
|
||||
console.log(customactions);
|
||||
var found = customactions.filter(item => item.Title == CustomActionTitle);
|
||||
if (found.length > 0) {
|
||||
this.setState({currentCustomAction:found[0]});
|
||||
var jsonproperties = found[0].ClientSideComponentProperties;
|
||||
|
||||
|
||||
var jsfileArray = JSON.parse(jsonproperties).jsfiles;
|
||||
var cssfileArray = JSON.parse(jsonproperties).cssfiles;
|
||||
|
||||
console.log(jsfileArray);
|
||||
console.log(cssfileArray);
|
||||
this.setState({jsfiles:jsfileArray,cssfiles:cssfileArray});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected async setCustomAction() {
|
||||
try {
|
||||
|
||||
const payload: TypedHash<string> = {
|
||||
"Title": CustomActionTitle,
|
||||
"Description": description,
|
||||
"Location": 'ClientSideExtension.ApplicationCustomizer',
|
||||
ClientSideComponentId:ApplicationCustomizerComponentID,
|
||||
ClientSideComponentProperties: JSON.stringify({jsfiles:this.state.jsfiles,cssfiles:this.state.cssfiles }),
|
||||
};
|
||||
|
||||
if(this.state.currentCustomAction == null) {
|
||||
const response : IUserCustomActionAddResult = await sp.web.userCustomActions.add(payload);
|
||||
console.log(response);
|
||||
const uca = await sp.web.userCustomActions.getById(response.data.Id);
|
||||
this.setState({currentCustomAction: uca,showMesssage:true,successmessage:"Application customizer activated sucessfully."});
|
||||
}
|
||||
else{
|
||||
const uca = sp.web.userCustomActions.getById(this.state.currentCustomAction.Id);
|
||||
const response: IUserCustomActionUpdateResult =await uca.update(payload);
|
||||
const ucaupdated = await sp.web.userCustomActions.getById(response.data.Id);
|
||||
this.setState({currentCustomAction: ucaupdated,showMesssage:true,successmessage:"Application customizer updated sucessfully."});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
|
||||
export interface IAddJsCssReferenceProps {
|
||||
description: string;
|
||||
context:WebPartContext;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"DescriptionFieldLabel": "Description Field"
|
||||
}
|
||||
});
|
10
samples/react-add-js-css-ref/src/webparts/addJsCssReference/loc/mystrings.d.ts
vendored
Normal file
10
samples/react-add-js-css-ref/src/webparts/addJsCssReference/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
declare interface IAddJsCssReferenceWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'AddJsCssReferenceWebPartStrings' {
|
||||
const strings: IAddJsCssReferenceWebPartStrings;
|
||||
export = strings;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-2.9/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib",
|
||||
"inlineSources": false,
|
||||
"strictNullChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"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-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,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue