Skip to content

Commit

Permalink
feat: copy to clipboard code snippet (scullyio#1060)
Browse files Browse the repository at this point in the history
* feat: copy to clipboard code snippet

* feat(scully): made loading clipboard.js lazy

ISSUES CLOSED: y

* test(scully): update snapshots

ISSUES CLOSED: y

* feat(scully): copy-to-clipboard plugin

* refactor(lib): move wrapper class into plugin

ISSUES CLOSED: y

* test(opy-to-clipboard): update snapshots

ISSUES CLOSED: y

* refactor: evert marked plugin to original, removed stale import

ISSUES CLOSED: y

Co-authored-by: sanderelias <[email protected]>
  • Loading branch information
ngdevelop-tech and SanderElias authored Oct 13, 2020
1 parent 5dd529a commit 70efac5
Show file tree
Hide file tree
Showing 17 changed files with 825 additions and 8 deletions.
515 changes: 515 additions & 0 deletions apps/scully-docs/src/assets/clipboard.min.js

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions apps/scully-docs/src/styles/code.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
font-size: 13px;
font-weight: 500;
border-radius: 4px;
overflow-x: auto;
}

.docs-page-content pre {
position: relative;
align-self: stretch;
margin-bottom: 24px;
padding: 16px 22px;
padding-right: 42px;
background: var(--scully-night);
overflow-x: auto;
}

.docs-page-content pre:not([class]) {
Expand All @@ -38,11 +37,12 @@
letter-spacing: 0.5px;
background: none;
box-shadow: none;
overflow-x: auto;
display: block;
padding-top: 20px;
}
.docs-page-content pre code::before {
.docs-page-content pre::before {
content: attr(class);
position: relative;
position: absolute;
display: block;
margin-bottom: 6px;
color: rgba(255, 255, 255, 0.5);
Expand Down
1 change: 1 addition & 0 deletions libs/plugins/scully-plugin-copy-to-clipboard/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"extends":"../../../.eslintrc","rules":{},"ignorePatterns":["!**/*"]}
54 changes: 54 additions & 0 deletions libs/plugins/scully-plugin-copy-to-clipboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Scully Copy To Clipboard Plugin

Copy to Clipboard Plugin add `copy` button in code snippets generated from markdown file with scully.

## Plugin Type

Render Plugin

## Usage

1. This plugin requires the `clipboard.min.js`. Download it from [here](https://clipboardjs.com/) and add it in `assets` folder of your application.

2. In the application's `scully.<your-app>.config.ts`, Import `CopyToClipboard` plugin and add it in `defaultPostHandlers`.

```typescript
import { CopyToClipboard } from '@scullyio/scully-plugin-copy-to-clipboard';

const defaultPostRenderers = [CopyToClipboard];

export const config: ScullyConfig = {
defaultPostRenderers,
routes: {
'/route/:slug': {
type: 'contentFolder',
postRenderers: [...defaultPostRenderers],
slug: {
folder: './folder',
},
},
},
// Other Configuration...
};
```

## Copy To Clipboard Plugin Configuration

Provide custom plugin configuration in application's `scully.<your-app>.config.ts`.

```typescript
setPluginConfig<CopyToClipboardPluginConfig>(CopyToClipboard, {
copyBtnInitialText: '📄',
copyBtnOnClickText: '',
customBtnClass: 'customClass',
clipboardJSPath:
'https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js',
});
```

| Property | Default | Description |
| ------------------ | -------------------------- | ------------------------------------------------------------- |
| customBtnClass | `''` | Add custom css class for `copy` button to apply styles |
| clipboardJSPath | `/assets/clipboard.min.js` | Specify clipboard js path, you can also specify CDN link here |
| copyBtnInitialText | `Copy` | `copy` button initial text |
| copyBtnOnClickText | `Copied!` | `copy` button text once code snippet is copied in clipboard |
14 changes: 14 additions & 0 deletions libs/plugins/scully-plugin-copy-to-clipboard/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
name: 'plugins-scully-plugin-copy-to-clipboard',
preset: '../../../jest.config.js',
globals: {
'ts-jest': {
tsConfig: '<rootDir>/tsconfig.spec.json',
},
},
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
coverageDirectory: '../../../coverage/libs/plugins/scully-plugin-copy-to-clipboard',
};
24 changes: 24 additions & 0 deletions libs/plugins/scully-plugin-copy-to-clipboard/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@scullyio/scully-plugin-copy-to-clipboard",
"version": "0.0.1",
"author": "Ankit Prajapati",
"license": "MIT",
"repository": {
"type": "GIT",
"url": "https://github.com/scullyio/scully/tree/main/libs/plugins/scully-plugin-copy-to-clipboard"
},
"peerDependencies": {
"@scullyio/scully": "*"
},
"dependencies": {
"jsdom": "^16.2.2"
},
"keywords": [
"angular",
"scully",
"prismjs",
"copy-to-clipboard",
"scully-plugin",
"plugin"
]
}
1 change: 1 addition & 0 deletions libs/plugins/scully-plugin-copy-to-clipboard/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib/plugins-scully-plugin-copy-to-clipboard';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { CopyToClipboard } from './plugins-scully-plugin-copy-to-clipboard';

describe('ScullyPluginCopyToClipboard', () => {
it('should work', () => {
expect(CopyToClipboard).toEqual('CopyToClipboard');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { registerPlugin, getPluginConfig, logWarn, yellow, HandledRoute } from '@scullyio/scully';
import { JSDOM } from 'jsdom';

export const CopyToClipboard = 'CopyToClipboard';

export interface CopyToClipboardPluginConfig {
/** add custom css class for copy button to apply styles */
customBtnClass?: string;
/** specify clipboard js path, default `/assets/clipboard.min.js` */
clipboardJSPath?: string;
/** copy button initial text, default `Copy` */
copyBtnInitialText?: string;
/** copy button text once code snippet is copied in clipboard , default `Copied!` */
copyBtnOnClickText?: string;
/** Selector that finds the code snippets, defaults to 'pre>code' */
selector?: string;
}

const defaultConfig: CopyToClipboardPluginConfig = {
customBtnClass: '',
clipboardJSPath: '/assets/clipboard.min.js',
copyBtnInitialText: 'Copy',
copyBtnOnClickText: 'Copied!',
selector: 'pre>code',
};

const copyToClipboardPlugin = async (html: string, options: HandledRoute): Promise<string> => {
const pluginConfig = { ...defaultConfig, ...getPluginConfig<CopyToClipboardPluginConfig>(CopyToClipboard) };

try {
const dom = new JSDOM(html);
const { window } = dom;
const { document } = window;

const scullyCodeSnippets = document.querySelectorAll(pluginConfig.selector);

/** Return unchanged HTML if document doesn't contain any code snippet */
if (!scullyCodeSnippets.length) {
return html;
}

/** Prepend copy to clipboard button on each code snippet pre */
scullyCodeSnippets.forEach((snippet) => {
/** get the parent as I now have the 'code' thing */
snippet = snippet.parentElement;
// return `<div class="scully-code-snippet" style="position:relative">${formattedCode}</div>`;

const wrapper = document.createElement('div');
wrapper.style.position = 'relative';
/** Copy to Clipboard Button Element */
const copyBtnEl = document.createElement('button');
copyBtnEl.className = 'copyToClipboard ' + pluginConfig.customBtnClass;
copyBtnEl.textContent = pluginConfig.copyBtnInitialText;

snippet.prepend(copyBtnEl);
snippet.parentNode.insertBefore(wrapper, snippet);
wrapper.appendChild(snippet);
});

/** append clipboard script to the body */
const clipboardScriptEl = document.createElement('script');
clipboardScriptEl.defer = true;
clipboardScriptEl.async = true;
clipboardScriptEl.setAttribute('sk', '');
clipboardScriptEl.innerHTML = `
const s = document.createElement('script');
s.src = '${pluginConfig.clipboardJSPath}';
s.addEventListener('load', () => registerCopyToClipboard());
s.addEventListener('error', () => console.warn('could not load "${pluginConfig.clipboardJSPath}", make sure you have it copied into your assets folder' ));
document.body.appendChild(s)
function registerCopyToClipboard() {
const clip = new ClipboardJS('pre .copyToClipboard', {
target: function (trigger) {
return trigger.nextElementSibling;
},
});
clip.on('success', function (event) {
event.trigger.textContent = '${pluginConfig.copyBtnOnClickText}';
event.clearSelection();
setTimeout(function () {
event.trigger.textContent = '${pluginConfig.copyBtnInitialText}';
}, 2000);
});
}
`;
/** append copy to clipboard button styles */
const styleEl = document.createElement('style');
styleEl.innerHTML = `
.copyToClipboard {
position: absolute;
right: 0;
top: 0;
padding: 5px;
color: rgb(27, 172, 78);
border-radius: 4px;
margin: 5px;
border: 1px solid rgb(27, 172, 78);
}
.copyToClipboard:hover {
background-color: rgb(27, 172, 78);
color: white;
border: 1px solid rgb(27, 172, 78);
}
`;

document.body.appendChild(clipboardScriptEl);
document.body.appendChild(styleEl);
return dom.serialize();
} catch (e) {
logWarn(`error in ${CopyToClipboard} Plugin, didn't parse for route "${yellow(options.route)}"`, e);
}

return html;
};

const validator = async () => [];

registerPlugin('render', CopyToClipboard, copyToClipboardPlugin, validator);
16 changes: 16 additions & 0 deletions libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"types": ["node", "jest"]
},
"include": [],
"files": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
12 changes: 12 additions & 0 deletions libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.lib.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../../dist/out-tsc",
"declaration": true,
"rootDir": "./src",
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"include": ["**/*.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.spec.js", "**/*.spec.jsx", "**/*.d.ts"]
}
3 changes: 3 additions & 0 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@
},
"plugins-scully-plugin-critical-css": {
"tags": []
},
"plugins-scully-plugin-copy-to-clipboard": {
"tags": []
}
},
"affected": {
Expand Down
3 changes: 2 additions & 1 deletion scully.scully-docs.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { docLink } from '@scullyio/scully-plugin-docs-link-update';
import { GoogleAnalytics } from '@scullyio/scully-plugin-google-analytics';
import { LogRocket } from '@scullyio/scully-plugin-logrocket';
import { Sentry } from '@scullyio/scully-plugin-sentry';
import { CopyToClipboard } from '@scullyio/scully-plugin-copy-to-clipboard';
import { removeScripts, RemoveScriptsConfig } from '@scullyio/scully-plugin-remove-scripts';
const marked = require('marked');
import { readFileSync } from 'fs-extra';
Expand All @@ -16,7 +17,7 @@ const { document } = window;

setPluginConfig('md', { enableSyntaxHighlighting: true });

const defaultPostRenderers = [LogRocket, GoogleAnalytics, removeScripts, 'seoHrefOptimise', criticalCSS];
const defaultPostRenderers = [LogRocket, GoogleAnalytics, removeScripts, 'seoHrefOptimise', criticalCSS, CopyToClipboard];

if (prod) {
/*
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"@scullyio/scully-plugin-google-analytics": ["libs/plugins/google-analytics/src/index.ts"],
"@scullyio/scully-plugin-logrocket": ["libs/plugins/logrocket/src/index.ts"],
"@scullyio/scully-plugin-remove-scripts": ["libs/plugins/scully-plugin-remove-scripts/src/index.ts"],
"@scullyio/scully-plugin-sentry": ["libs/plugins/sentry/src/index.ts"]
"@scullyio/scully-plugin-sentry": ["libs/plugins/sentry/src/index.ts"],
"@scullyio/scully-plugin-copy-to-clipboard": ["libs/plugins/scully-plugin-copy-to-clipboard/src/index.ts"]
}
},
"angularCompilerOptions": {
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"@scullyio/scully-plugin-logrocket": ["libs/plugins/logrocket/src/index.ts"],
"@scullyio/scully-plugin-sentry": ["libs/plugins/sentry/src/index.ts"],
"@scullyio/scully-plugin-critical-css": ["libs/plugins/scully-plugin-critical-css/src/index.ts"],
"@scullyio/scully-plugin-remove-scripts": ["libs/plugins/scully-plugin-remove-scripts/src/index.ts"]
"@scullyio/scully-plugin-remove-scripts": ["libs/plugins/scully-plugin-remove-scripts/src/index.ts"],
"@scullyio/scully-plugin-copy-to-clipboard": ["libs/plugins/scully-plugin-copy-to-clipboard/src/index.ts"]
}
},
"angularCompilerOptions": {
Expand Down
37 changes: 37 additions & 0 deletions workspace.json
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,43 @@
}
}
}
},
"plugins-scully-plugin-copy-to-clipboard": {
"root": "libs/plugins/scully-plugin-copy-to-clipboard",
"sourceRoot": "libs/plugins/scully-plugin-copy-to-clipboard/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": [
"libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.lib.json",
"libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/plugins/scully-plugin-copy-to-clipboard/**/*"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/plugins/scully-plugin-copy-to-clipboard/jest.config.js",
"tsConfig": "libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.spec.json",
"passWithNoTests": true
}
},
"build": {
"builder": "@nrwl/node:package",
"options": {
"outputPath": "dist/libs/scully-plugin-copy-to-clipboard",
"tsConfig": "libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.lib.json",
"packageJson": "libs/plugins/scully-plugin-copy-to-clipboard/package.json",
"main": "libs/plugins/scully-plugin-copy-to-clipboard/src/index.ts",
"assets": ["libs/plugins/scully-plugin-copy-to-clipboard/*.md"]
}
}
}
}
},
"cli": {
Expand Down

0 comments on commit 70efac5

Please sign in to comment.