UNPKG

7.73 kBPlain TextView Raw
1<script>
2/* eslint-disable vue/no-v-html */
3
4import * as Babel from '@babel/standalone';
5import BootstrapVue from 'bootstrap-vue';
6import copyToClipboard from 'copy-to-clipboard';
7import hljs from 'highlight.js';
8import { html } from 'js-beautify';
9import Vue from 'vue';
10import { parseComponent } from 'vue-template-compiler';
11import 'highlight.js/styles/monokai.css';
12import { gitlabComponents } from '../all_components';
13import * as Documentation from '../components_documentation';
14
15// We need to register globally all components as we don't know the components that are used in the dynamically compiled .example.vue files
16// This is only for design.gitlab.com and shouldn't be done in our actual application
17Vue.use(BootstrapVue);
18Object.keys(gitlabComponents).forEach((comp) => {
19 if (!comp.includes('Directive')) {
20 Vue.component(comp, gitlabComponents[comp]);
21 }
22});
23
24// We need to do Directives for now manually
25Vue.directive('gl-tooltip', gitlabComponents.GlTooltipDirective);
26Vue.directive('gl-modal-directive', gitlabComponents.GlModalDirective);
27Vue.directive('gl-resize-observer-directive', gitlabComponents.GlResizeObserverDirective);
28Vue.directive('gl-safe-html-directive', gitlabComponents.GlSafeHtmlDirective);
29Vue.directive('gl-collapse-toggle', gitlabComponents.GlCollapseToggleDirective);
30
31function findComponentExample(exampleName) {
32 /* eslint-disable no-restricted-syntax */
33 // Doing it with a for loop to have an early return/break during iteration
34 for (const component of Object.values(Documentation)) {
35 if (component.examples) {
36 // Looking for an example in the component documentation definition that matches the exampleName
37 for (const exampleGroup of component.examples) {
38 const foundExample = exampleGroup.items.find((example) => example.id === exampleName);
39 if (foundExample) {
40 return foundExample;
41 }
42 }
43 }
44 }
45 /* eslint-enable no-restricted-syntax */
46 return null;
47}
48
49const animationTimeout = 2000;
50
51export default {
52 props: {
53 exampleName: {
54 type: String,
55 required: true,
56 },
57 },
58 data() {
59 return {
60 source: '',
61 renderedHtml: '',
62 copiedSource: false,
63 copiedOutput: false,
64 };
65 },
66 computed: {
67 // Based on the set exampleName we try to look it up in the currently loaded Documentation Examples and then read it-> parse and compile it
68 currentExampleComponent() {
69 // Going through all documentation objects of all components
70 const foundExample = findComponentExample(this.exampleName);
71
72 // Live loading of .example.vue files
73 // Examples are included with webpack through raw-loader -> Results in a string
74 if (foundExample && foundExample !== undefined) {
75 const base = {
76 name: '',
77 template: '<div></div>',
78 };
79 try {
80 // At Runtime we parse it with `parseComponent` from `vue-template-compiler` (Line 64)
81 const parsed = parseComponent(foundExample.component);
82
83 let compiled = {};
84 if (parsed.script) {
85 // We parse the script part with Babel.transform (Line 68 in example_display)
86 const { code } = Babel.transform(parsed.script.content, {
87 presets: ['es2015', ['stage-2', { decoratorsBeforeExport: false }]],
88 });
89 compiled = eval(`const exports = {};${code}`); // eslint-disable-line no-eval
90 }
91
92 // Results in the original source of the template and the compiled and working Vue component
93 compiled.template = parsed.template.content;
94 // eslint-disable-next-line vue/no-side-effects-in-computed-properties
95 this.source = compiled.template;
96
97 return { ...base, ...compiled };
98 } catch (e) {
99 // eslint-disable-next-line no-console
100 console.log('ERR : ', e);
101 return base;
102 }
103 }
104 return null;
105 },
106 exampleHTMLOutput() {
107 return this.$refs.exampleComponent || '';
108 },
109 sourceFormatted() {
110 if (this.source) {
111 return hljs.fixMarkup(hljs.highlight('html', this.source).value);
112 }
113 return '';
114 },
115 renderedHtmlFormatted() {
116 if (this.renderedHtml) {
117 return hljs.fixMarkup(hljs.highlight('html', this.renderedHtml).value);
118 }
119 return '';
120 },
121 },
122 watch: {
123 currentExampleComponent() {
124 Vue.nextTick(() => {
125 this.setHtml();
126 });
127 },
128 },
129 created() {
130 // Converting Tabs to spaces to make display look indented
131 hljs.configure({ tabReplace: ' ', useBR: true });
132 },
133 mounted() {
134 this.setHtml();
135 },
136 methods: {
137 setHtml() {
138 if (this.$refs.compiled) {
139 const markup = this.$refs.compiled.$el.outerHTML;
140 if (markup) {
141 // Splitting lines for easier readability on the rendered output
142 const preFormattedHTML = markup.replace(/><(?!\/i|\/label|\/span|option)/g, '>\n<');
143 this.renderedHtml = html(preFormattedHTML, {
144 indent_size: 2,
145 indent_char: ' ',
146 wrap_attributes: 'auto',
147 wrap_attributes_indent_size: 2,
148 end_with_newline: false,
149 });
150 }
151 }
152 },
153 copySource() {
154 copyToClipboard(this.source);
155 this.copiedSource = true;
156 setTimeout(() => {
157 this.copiedSource = false;
158 }, animationTimeout);
159 },
160 copyHtml() {
161 copyToClipboard(this.renderedHtml);
162 this.copiedOutput = true;
163 setTimeout(() => {
164 this.copiedOutput = false;
165 }, animationTimeout);
166 },
167 },
168};
169</script>
170
171<template>
172 <div>
173 <div v-if="currentExampleComponent">
174 <b-card no-body>
175 <div slot="header">
176 <b-row>
177 <b-col>
178 <strong>{{ exampleName }}</strong>
179 </b-col>
180 <b-col class="text-right">
181 <b-button-group size="sm" class="mx-1">
182 <b-btn v-b-toggle.collapseSource>Source</b-btn>
183 <b-btn v-b-toggle.collapseHTML>HTML</b-btn>
184 </b-button-group>
185 </b-col>
186 </b-row>
187 </div>
188 <b-card-body>
189 <div :is="currentExampleComponent" ref="compiled" />
190 </b-card-body>
191 <b-list-group flush>
192 <b-collapse id="collapseSource" class="mt-2">
193 <b-list-group-item>
194 <b-row>
195 <b-col cols="8"><h6>Source</h6></b-col>
196 <b-col class="text-right">
197 <template v-if="copiedSource">Copied!</template>
198 <b-button-group size="sm">
199 <b-button :disabled="copiedSource" @click="copySource">Copy</b-button>
200 </b-button-group>
201 </b-col>
202 </b-row>
203 <code class="hljs html" v-html="sourceFormatted"></code>
204 </b-list-group-item>
205 </b-collapse>
206 <b-collapse id="collapseHTML" class="mt-2">
207 <b-list-group-item>
208 <b-row>
209 <b-col cols="8"><h6>HTML Output</h6></b-col>
210 <b-col class="text-right">
211 <template v-if="copiedOutput">Copied!</template>
212 <b-button-group size="sm">
213 <b-button :disabled="copiedOutput" @click="copyHtml">Copy</b-button>
214 </b-button-group>
215 </b-col>
216 </b-row>
217 <code class="hljs html" v-html="renderedHtmlFormatted"></code>
218 </b-list-group-item>
219 </b-collapse>
220 </b-list-group>
221 </b-card>
222 </div>
223 <b-alert v-else-if="exampleName" show variant="warning"
224 >No Example found with the name "{{ exampleName }}"</b-alert
225 >
226 </div>
227</template>