├── src
└── snippet.js
├── config.json
├── .editorconfig
├── dist
└── js
│ └── plugin-node-uiextension.js
├── .gitignore
├── package.json
├── LICENSE
├── .eslintrc
├── README.md
└── index.js
/src/snippet.js:
--------------------------------------------------------------------------------
1 | $('
<>'),
2 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "stylesheets":["../../../css/pattern-scaffolding.css"],
3 | "navLinks": {
4 | "before": [],
5 | "after": []
6 | },
7 | "gearLinks": {
8 | "before": [],
9 | "beforeSearch": []
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | tab_width = 2
8 | end_of_line = lf
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 |
--------------------------------------------------------------------------------
/dist/js/plugin-node-uiextension.js:
--------------------------------------------------------------------------------
1 | /* global PluginUIExtension, $ */
2 | var PluginUIExtension = {
3 |
4 | /**
5 | * The function defined as the onready callback within the plugin configuration.
6 | */
7 | init: function () {
8 | var $nav = $('#pl-pattern-nav-target');
9 | $nav.prepend(/*NAVLINKS-BEFORE-SNIPPET*/);
10 | $nav.append(/*NAVLINKS-AFTER-SNIPPET*/);
11 |
12 | var $rightList = $('.sg-checklist');
13 | $rightList.prepend(/*GEARLINKS-BEFORE-SNIPPET*/);
14 | $rightList.find('#sg-find').before(/*GEARLINKS-BEFORESEARCH-SNIPPET*/);
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plugin-node-uiextension",
3 | "version": "1.0.0-alpha",
4 | "description": "Provide a simple PL chrome customization path versus forking styleguide-assets-default",
5 | "main": "index.js",
6 | "dependencies": {
7 | "fs-extra": "^0.30.0",
8 | "glob": "^7.0.0",
9 | "lodash": "~4.13.1"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/bmuenzenmeyer/plugin-node-uiextension.git"
14 | },
15 | "scripts": {
16 | "test": "eslint dist/js/plugin-node-uiextension.js index.js"
17 | },
18 | "author": "Brian Muenzenmeyer",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/bmuenzenmeyer/plugin-node-uiextension/issues"
22 | },
23 | "homepage": "https://github.com/bmuenzenmeyer/plugin-node-uiextension",
24 | "devDependencies": {
25 | "eslint": "^3.5.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Brian Muenzenmeyer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "builtin": true,
5 | "es6": true
6 | },
7 | "parserOptions": {
8 | "ecmaVersion": 6,
9 | "sourceType": "module"
10 | },
11 | "globals": {},
12 | "rules": {
13 | "block-scoped-var": 0,
14 | "camelcase": 0,
15 | "comma-spacing": [1, {"before": false, "after": true}],
16 | "consistent-return": 2,
17 | "curly": [2, "all"],
18 | "dot-notation": [1, { "allowKeywords": true }],
19 | "eqeqeq": [2, "allow-null"],
20 | "global-strict": [0, "never"],
21 | "guard-for-in": 2,
22 | "indent": [1, 2, {"SwitchCase": 1, "VariableDeclarator": 1}],
23 | "lines-around-comment": [1, {
24 | "beforeBlockComment": true,
25 | "beforeLineComment": true,
26 | "allowBlockStart": true,
27 | "allowObjectStart": true,
28 | "allowArrayStart": true
29 | }],
30 | "key-spacing": 0,
31 | "keyword-spacing": 1,
32 | "new-cap": 0,
33 | "no-alert": 2,
34 | "no-bitwise": 2,
35 | "no-caller": 2,
36 | "no-cond-assign": [2, "except-parens"],
37 | "no-debugger": 2,
38 | "no-dupe-args": 2,
39 | "no-dupe-keys": 2,
40 | "no-empty": 2,
41 | "no-eval": 2,
42 | "no-extend-native": 2,
43 | "no-extra-bind": 2,
44 | "no-extra-parens": 0,
45 | "no-extra-semi": 2,
46 | "no-func-assign": 2,
47 | "no-implied-eval": 2,
48 | "no-invalid-regexp": 2,
49 | "no-irregular-whitespace": 1,
50 | "no-iterator": 2,
51 | "no-loop-func": 2,
52 | "no-mixed-requires": 0,
53 | "no-multi-str": 2,
54 | "no-multi-spaces": 1,
55 | "no-native-reassign": 2,
56 | "no-new": 2,
57 | "no-param-reassign": 1,
58 | "no-proto": 2,
59 | "no-redeclare": 0,
60 | "no-script-url": 2,
61 | "no-self-assign": 2,
62 | "no-self-compare": 2,
63 | "no-sequences": 2,
64 | "no-shadow": 0,
65 | "no-undef": 2,
66 | "no-underscore-dangle": 0,
67 | "no-unreachable": 1,
68 | "no-unused-vars": 1,
69 | "no-use-before-define": 1,
70 | "no-useless-call": 2,
71 | "no-useless-concat": 2,
72 | "no-with": 2,
73 | "quotes": [0, "single"],
74 | "radix": 2,
75 | "semi": [1, "always"],
76 | "strict": 0,
77 | "space-before-blocks": 1,
78 | "space-before-function-paren": [1, {
79 | "anonymous": "always",
80 | "named": "never"
81 | }],
82 | "space-in-parens": [1, "never"],
83 | "space-infix-ops": 1,
84 | "valid-typeof": 2,
85 | "vars-on-top": 0,
86 | "wrap-iife": [2, "inside"],
87 | "prefer-const": ["error", {
88 | "destructuring": "any",
89 | "ignoreReadBeforeAssign": false
90 | }]
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | [](https://www.npmjs.com/package/plugin-node-uiextension)
3 | [](https://gitter.im/pattern-lab/node)
4 |
5 | # UI Extension Plugin for Pattern Lab Node
6 |
7 | The UI Extension plugin allows users to customize the Pattern Lab frontend style guide without having to fork [styleguide-assets-default](https://github.com/pattern-lab/styleguidekit-assets-default). It is intended for styling overrides and navigation additions. If you need anything further, it's suggested that you fork the `styleguide-assets-default` repo and consume your own custom frontend.
8 |
9 | 
10 |
11 | ## Installation
12 |
13 | To add the UI Extension Plugin to your project using [npm](http://npmjs.com/) type:
14 |
15 | npm install plugin-node-uiextension --save
16 |
17 | Or add it directly to your project's `package.json` file and run `npm install`
18 |
19 | During installation, the plugin is added as a key to the `plugins` object in your main Pattern Lab project's `patternlab-config.json` file
20 |
21 | > If you don't see this object, try running `npm run postinstall` within the root of your project.
22 |
23 | ## Configuration
24 |
25 | Post-installation, you will see the following in your `patternlab-config.json`:
26 |
27 | Example:
28 |
29 | ```
30 | "plugins": {
31 | "plugin-node-uiextension": {
32 | "enabled": true,
33 | "initialized": false,
34 | "options": {
35 | "stylesheets": [
36 | "../../../css/pattern-scaffolding.css"
37 | ],
38 | "navLinks": {
39 | "before": [],
40 | "after": []
41 | },
42 | "gearLinks": {
43 | "before": [],
44 | "beforeSearch": []
45 | }
46 | }
47 | }
48 | }
49 | ```
50 |
51 | ### CSS
52 |
53 | Note the defaulted `pattern-scaffolding.css` file, which is relative to the installation location within the `/public/` output.
54 |
55 | > At this time, loading external CSS is not supported.
56 |
57 | This file is already responsible for meta-styling of your patterns, and is usually only scoped to the viewer ``. With this default, you now have a useful CSS file for altering both the Pattern Lab UI inside the ish `` as well as the main frontend. You can use a [mockup of Pattern Lab on Codepen](http://codepen.io/bmuenzenmeyer/pen/791da488b2a73909a58eacf801af83d4) to alter the look and feel, and then export or append the **compiled css** back into `pattern-scaffolding.css`.
58 |
59 | Here's a [Pattern Lab light theme](http://codepen.io/bmuenzenmeyer/pen/813a628ae7185fed6137cc2498e74df5) quickly created using the CodePen above.
60 |
61 | This is also a good way to build [custom pattern states](http://patternlab.io/docs/pattern-states.html#node) and have their colors represented on the UI.
62 |
63 | #### Adding Links
64 |
65 | A `navLinks` and `gearLinks` object are also initialized post-installation, and allow you to add arbitrary anchor tags to the front end in various locations.
66 |
67 | For example, adding the following snippet:
68 |
69 | ```
70 | ...
71 | "navLinks": {
72 | "before": [
73 | { "text": "Voice and Tone", "url": "http://example.com/writing-guide", "class": ""}
74 | ],
75 | "after": [
76 | { "text": "Contribute", "url": "http://example.com/contribute", "class": ""},
77 | { "text": "Downloads", "url": "http://example.com/resources", "class": ""}
78 | ]
79 | },
80 | ...
81 | ```
82 |
83 | would add a link to the `Voice and Tone` before the main navigation, with a `Contribute` and `Downloads` link to follow.
84 |
85 | ## Enabling / Disabling the Plugin
86 |
87 | After install, you may manually enable or disable the plugin by finding the `plugin-node-uiextension` key within your main Pattern Lab project's `patternlab-config.json` file and setting the `enabled` flag. In the future this will be possible via CLI.
88 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const pluginName = 'plugin-node-uiextension';
4 |
5 | const fs = require('fs-extra');
6 | const glob = require('glob');
7 | const path = require('path');
8 | const _ = require('lodash');
9 |
10 | function writeConfigToOutput(patternlab, pluginConfig) {
11 | var pluginConfigPathName = path.resolve(patternlab.config.paths.public.root, 'patternlab-components', 'packages');
12 | try {
13 | fs.outputFileSync(pluginConfigPathName + '/' + pluginName + '.json', JSON.stringify(pluginConfig, null, 2));
14 | } catch (ex) {
15 | console.trace(pluginName + ': Error occurred while writing pluginFile configuration');
16 | console.log(ex);
17 | }
18 | }
19 |
20 | /**
21 | * Define what events you wish to listen to here
22 | * For a full list of events - check out https://github.com/pattern-lab/patternlab-node/wiki/Creating-Plugins#events
23 | * @param patternlab - global data store which has the handle to the event emitter
24 | */
25 | function registerEvents(patternlab) {
26 | //register our handler at the appropriate time of execution
27 | }
28 |
29 | /**
30 | * A single place to define the frontend configuration
31 | * This configuration is outputted to the frontend explicitly as well as included in the plugins object.
32 | *
33 | */
34 | function getPluginFrontendConfig() {
35 | var defaults = {
36 | "name":"pattern-lab\/plugin-node-uiextension",
37 | "templates":[],
38 | "stylesheets":[],
39 | "javascripts":[
40 | "patternlab-components\/pattern-lab\/" + pluginName + "\/js\/" + pluginName + ".js"
41 | ],
42 | "onready":"PluginUIExtension.init()",
43 | "callback":""
44 | };
45 |
46 | var pluginConfig = require('./config.json');
47 | return _.extend({}, defaults, pluginConfig);
48 | }
49 |
50 | /**
51 | * Creates a link from the passed in data
52 | */
53 | function createLink(link, template) {
54 | return template.replace('<>', link.class).replace('<>', link.url).replace('<>', link.text);
55 | }
56 |
57 | /**
58 | * Replaces the snippet placeholder with actual content
59 | */
60 | function fillPlaceholder(file, placeholder, snippet){
61 | snippet = snippet.replace(/,\s*$/, '');
62 | return file.replace(placeholder, snippet);
63 | }
64 |
65 | /**
66 | * The entry point for the plugin. You should not have to alter this code much under many circumstances.
67 | * Instead, alter getPluginFrontendConfig() and registerEvents() methods
68 | */
69 | function pluginInit(patternlab) {
70 |
71 | if (!patternlab) {
72 | console.error('patternlab object not provided to plugin-init');
73 | process.exit(1);
74 | }
75 |
76 | //write the plugin json to public/patternlab-components
77 | var pluginConfig = getPluginFrontendConfig();
78 | pluginConfig.stylesheets = patternlab.config.plugins[pluginName].options.stylesheets;
79 | pluginConfig.navLinks = patternlab.config.plugins[pluginName].options.navLinks;
80 | pluginConfig.gearLinks = patternlab.config.plugins[pluginName].options.gearLinks;
81 | writeConfigToOutput(patternlab, pluginConfig);
82 |
83 | //add the plugin config to the patternlab-object for later export
84 | if (!patternlab.plugins) {
85 | patternlab.plugins = [];
86 | }
87 | patternlab.plugins.push(pluginConfig);
88 |
89 | //write the plugin dist folder to public/pattern-lab
90 | var pluginFiles = glob.sync(__dirname + '/dist/**/*');
91 |
92 | if (pluginFiles && pluginFiles.length > 0) {
93 |
94 | const link_frontend_snippet = fs.readFileSync(path.resolve(__dirname + '/src/snippet.js'), 'utf8');
95 |
96 | for (var i = 0; i < pluginFiles.length; i++) {
97 | try {
98 | var fileStat = fs.statSync(pluginFiles[i]);
99 | if (fileStat.isFile()) {
100 | var relativePath = path.relative(__dirname, pluginFiles[i]).replace('dist', ''); //dist is dropped
101 | var writePath = path.join(patternlab.config.paths.public.root, 'patternlab-components', 'pattern-lab', pluginName, relativePath);
102 |
103 | //we need to alter the dist file to add links for us
104 | //we are also being a bit lazy here, since we only expect one file
105 | let uiextensionJSFileContents = fs.readFileSync(pluginFiles[i], 'utf8');
106 | let snippetString = '';
107 |
108 | //construct our links from the snippets
109 | if (pluginConfig.navLinks) {
110 | if (pluginConfig.navLinks.before && pluginConfig.navLinks.before.length > 0) {
111 | for (var n = 0; n < pluginConfig.navLinks.before.length; n++) {
112 | snippetString += createLink(pluginConfig.navLinks.before[n], link_frontend_snippet);
113 | }
114 | uiextensionJSFileContents = fillPlaceholder(uiextensionJSFileContents, '/*NAVLINKS-BEFORE-SNIPPET*/', snippetString);
115 | }
116 |
117 | snippetString = '';
118 | if (pluginConfig.navLinks.after && pluginConfig.navLinks.after.length > 0) {
119 | for (var j = 0; j < pluginConfig.navLinks.after.length; j++) {
120 | snippetString += createLink(pluginConfig.navLinks.after[j], link_frontend_snippet);
121 | }
122 | uiextensionJSFileContents = fillPlaceholder(uiextensionJSFileContents, '/*NAVLINKS-AFTER-SNIPPET*/', snippetString);
123 | }
124 | }
125 |
126 | if (pluginConfig.gearLinks) {
127 | snippetString = '';
128 | if (pluginConfig.gearLinks.before && pluginConfig.gearLinks.before.length > 0) {
129 | for (var k = 0; k < pluginConfig.gearLinks.before.length; k++) {
130 | snippetString += createLink(pluginConfig.gearLinks.before[k], link_frontend_snippet);
131 | }
132 | uiextensionJSFileContents = fillPlaceholder(uiextensionJSFileContents, '/*GEARLINKS-BEFORE-SNIPPET*/', snippetString);
133 | }
134 |
135 | snippetString = '';
136 | if (pluginConfig.gearLinks.beforeSearch && pluginConfig.gearLinks.beforeSearch.length > 0) {
137 | for (var m = 0; m < pluginConfig.gearLinks.beforeSearch.length; m++) {
138 | snippetString += createLink(pluginConfig.gearLinks.beforeSearch[m], link_frontend_snippet);
139 | }
140 | uiextensionJSFileContents = fillPlaceholder(uiextensionJSFileContents, '/*GEARLINKS-BEFORESEARCH-SNIPPET*/', snippetString);
141 | }
142 | }
143 |
144 | fs.outputFileSync(writePath, uiextensionJSFileContents);
145 | }
146 | } catch (ex) {
147 | console.trace(pluginName + ': Error occurred while copying pluginFile', pluginFiles[i]);
148 | console.log(ex);
149 | }
150 | }
151 | }
152 |
153 | //setup listeners if not already active
154 | if (patternlab.config[pluginName] !== undefined && !patternlab.config[pluginName]) {
155 |
156 | //register events
157 | registerEvents(patternlab);
158 |
159 | //set the plugin key to true to indicate it is installed and ready
160 | patternlab.config[pluginName] = true;
161 | }
162 |
163 | }
164 |
165 | module.exports = pluginInit;
166 |
--------------------------------------------------------------------------------