├── .gitignore ├── .gitattributes ├── src ├── ui │ ├── scripts │ │ ├── helper-functions.js │ │ └── scripts.js │ ├── styles │ │ └── styles.scss │ └── index.html ├── manifest.json └── main │ └── code.ts ├── dist ├── manifest.json ├── code.js └── index.html ├── static └── plugin-experiment-docs.gif ├── tsconfig.json ├── package.json ├── README.md └── gulpfile.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | package-lock.json -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /src/ui/scripts/helper-functions.js: -------------------------------------------------------------------------------- 1 | function helper() { 2 | console.log('testing'); 3 | } -------------------------------------------------------------------------------- /dist/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Docs Plugin Demo", 3 | "id": "", 4 | "api": "1.0.0", 5 | "main": "code.js", 6 | "ui": "index.html" 7 | } -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Docs Plugin Demo", 3 | "id": "", 4 | "api": "1.0.0", 5 | "main": "code.js", 6 | "ui": "index.html" 7 | } -------------------------------------------------------------------------------- /static/plugin-experiment-docs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adispezio/figma-plugin-external-docs-experiment/HEAD/static/plugin-experiment-docs.gif -------------------------------------------------------------------------------- /src/ui/styles/styles.scss: -------------------------------------------------------------------------------- 1 | /* Add your own scripts */ 2 | html, body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | iframe { 8 | width: 100%; 9 | height: 100%; 10 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "typeRoots": [ 5 | "./node_modules/@types", 6 | "./node_modules/@figma" 7 | ] 8 | }, 9 | } -------------------------------------------------------------------------------- /src/ui/scripts/scripts.js: -------------------------------------------------------------------------------- 1 | const docFrame = document.querySelector('#docFrame'); 2 | const srcURI = 'https://primer.style/components/' 3 | 4 | onmessage = (event) => { 5 | updateiFrame(event.data.pluginMessage); 6 | } 7 | 8 | function updateiFrame(string) { 9 | const regex = /(.*@api\/components\/)(.*)/; 10 | const found = string.match(regex); 11 | //console.log(found[2]); 12 | 13 | docFrame.src = srcURI + found[2]; 14 | } 15 | 16 | function updateURL(component) { 17 | docFrame.src = component; 18 | } -------------------------------------------------------------------------------- /src/ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
3 |
4 | A plugin experiment to show related external documentation for a selected component/instance.
5 |
6 | ## How it works
7 | The plugin reads the component description of the currently selected component and searches for the term `@api` (this could be anything). If there's a match, it uses the component name to serve the corresponding page of the documentation (by simple URL matching).
8 |
9 | I've used Github Primer and the Primer docs as an example as this requires cross-origin requests and not a lot of public styleguides have this enabled :)
10 |
11 | ## Thoughts
12 | This is a very simple approach to a very powerful concept. A lot of design systems and/or dev teams have existing docs that would be valuable to reference while designing - why not pull them straight in!
13 |
14 | There's a lot of fragility and room for human error by matching text strings and I could imagine a much more robust approach with:
15 | - uuid references instead of text
16 | - actual API request for urls
17 | - two-way data to populate the Figma canvas with useful info from the docs
18 | - local and external docs
19 |
20 | ## Next steps
21 | I don't have any further plans for this experiment - but perhaps it will be useful for someone else!
22 |
23 | ## Thanks
24 | - [@thomas-lowry](https://github.com/thomas-lowry) and the invaluable [Figma Plugin Boilerplate (FPB)](https://github.com/thomas-lowry/figma-plugin-boilerplate#intro)
25 | - The [Primer](https://github.com/primer) team for their amazing [styleguide](https://primer.style/) and their [Primer Web Figma Community File](https://www.figma.com/community/file/854767373644076713)
26 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | // Initialize gulp + modules
2 | const { src, dest, watch, series, parallel } = require('gulp');
3 |
4 | // gulp plugins
5 | const sass = require('gulp-sass'); //import SCSS compiler
6 | const concat = require('gulp-concat'); //enables gulp to concatenate multiple JS files into one
7 | const minify = require('gulp-minify'); //minifies JS
8 | const postcss = require('gulp-postcss'); //handles processing of CSS and enables use of cssnano and autoprefixer
9 | const autoprefixer = require('autoprefixer'); //handles autoprefixing for browser support
10 | const csso = require('gulp-csso'); //css minification
11 | const ts = require('gulp-typescript'); //typescript compiler
12 | const replace = require('gulp-replace'); //replace a string in a file being processed
13 | const base64 = require('gulp-base64-inline'); //inline any css background images with base64
14 | const inlinesource = require('gulp-inline-source'); //inline js and css and images
15 | const htmlmin = require('gulp-htmlmin'); //minify html
16 |
17 | //for signalling dev vs. prod build
18 | const util = require('gulp-util'); //enables a dev and production build with minification
19 | var production = !!util.env.production; //this keeps track of whether or not we are doing a normal or priduction build
20 |
21 | //clean up post build
22 | const purgecss = require('gulp-purgecss'); //remove unused css
23 | const del = require('del'); //plugin to delete temp files after build
24 |
25 | // TS Config
26 | const tsProject = ts.createProject('tsconfig.json', { noImplicitAny: true, outFile: 'code.js' });
27 |
28 | // File paths
29 | const files = {
30 | scssPath: 'src/ui/styles/**/*.scss', //path to your CSS/SCSS folder
31 | jsPath: 'src/ui/scripts/**/*.js', //path to any javascript that you use in your UI
32 | tsPath: 'src/main/**/*.ts', //location of typescript files for the main plugin code that interfaces with the Figma API
33 | html: 'src/ui/index.html', //this is your main index file where you will create your UI markup
34 | manifest: 'src/manifest.json', //location of manifest file
35 | assetsPath: 'src/ui/img/*.{png,gif,jpg,svg,jpeg}' //path to image assets for your UI
36 | }
37 |
38 | // SCSS task: compiles the styles.scss file into styles.css
39 | function scssTask(){
40 | return src(files.scssPath)
41 | .pipe(sass()) //compile to css
42 | .pipe(replace('background-image: url(', 'background-image: inline('))
43 | .pipe(base64('')) //base 64 encode any background images
44 | .pipe(postcss([ autoprefixer()])) // PostCSS plugins
45 | .pipe(production ? csso() : util.noop()) //minify css on production build
46 | .pipe(dest('src/ui/tmp') //put in temporary directory
47 | );
48 | }
49 |
50 | //CSS Task: Process Figma Plugin DS CSS
51 | function cssTask() {
52 | return src('node_modules/figma-plugin-ds/dist/figma-plugin-ds.css')
53 | .pipe(production ? purgecss({
54 | content: ['src/ui/index.html', 'src/ui/tmp/scripts.js'],
55 | whitelistPatterns: [/select-menu(.*)/],
56 | }) : util.noop()) //remove unused CSS
57 | .pipe(production ? csso() : util.noop()) //minify css on production build
58 | .pipe(dest('src/ui/tmp') //put in temporary directory
59 | );
60 | }
61 |
62 | // JS task: concatenates JS files to scripts.js (minifies on production build)
63 | function jsTask(){
64 | return src(['node_modules/figma-plugin-ds/dist/iife/figma-plugin-ds.js', files.jsPath])
65 | .pipe(concat('scripts.js'))
66 | .pipe(dest('src/ui/tmp')
67 | );
68 | }
69 |
70 | //TS task: compiles the typescript main code that interfaces with the figma plugin API
71 | function tsTask() {
72 | return src([files.tsPath])
73 | .pipe(tsProject())
74 | .pipe(production ? minify({
75 | ext: {
76 | min: '.js'
77 | },
78 | noSource: true
79 | }) : util.noop())
80 | .pipe(dest('dist'));
81 | }
82 |
83 | //HTML task: copies and minifies
84 | function htmlTask() {
85 | return src([files.html])
86 | .pipe(inlinesource({
87 | attribute: false,
88 | compress: production ? true : false,
89 | pretty: true
90 | }))
91 | .pipe(production ? htmlmin({ collapseWhitespace: true }) : util.noop())
92 | .pipe(dest('dist'));
93 | }
94 |
95 | //Clean up temporary files
96 | function cleanUp() {
97 | return del(['src/ui/tmp']);
98 | }
99 |
100 | //copy manifest file to dist
101 | function manifestTask() {
102 | return src([files.manifest])
103 | .pipe(dest('dist')
104 | );
105 | }
106 |
107 |
108 | // Watch all key files for changes, if there is a change saved, create a build
109 | function watchTask(){
110 | watch([files.scssPath, files.jsPath, files.tsPath, files.html, files.manifest],
111 | {interval: 1000, usePolling: true},
112 | series(
113 | parallel(jsTask, tsTask),
114 | scssTask,
115 | cssTask,
116 | htmlTask,
117 | manifestTask,
118 | cleanUp
119 | )
120 | );
121 | }
122 |
123 | // Export the default Gulp task so it can be run
124 | // Runs the scss, js, and typescript tasks simultaneously
125 | exports.default = series(
126 | parallel(jsTask, tsTask),
127 | scssTask,
128 | cssTask,
129 | htmlTask,
130 | manifestTask,
131 | cleanUp,
132 | watchTask
133 | );
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
2057 |
2068 |
2069 |
2070 |
2071 |