├── .editorconfig
├── .gitignore
├── README.md
├── gulpfile.js
├── package.json
└── src
├── blocks
├── footer.html
└── header.html
├── index.html
├── js
├── script.js
└── tinylib.js
└── scss
├── _base.scss
├── _mixins.scss
├── _normalize.scss
├── _vars.scss
├── blocks
├── _attrs.scss
├── _code.scss
├── _flags.scss
├── _footer.scss
├── _github.scss
├── _header.scss
├── _panel.scss
├── _popup.scss
├── _svg.scss
└── _wave-types.scss
└── styles.scss
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .sass-cache/
3 | .publish/
4 | node_modules/
5 | build/
6 | assets/
7 | npm-debug.log
8 | index.html
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Wave maker
2 |
3 | Tool based on [SVG Arcs](https://www.w3.org/TR/SVG2/paths.html#PathDataEllipticalArcCommands). Let you understand SVG Arcs better and create some funny waves
4 |
5 | https://yoksel.github.io/wave-maker/
6 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var sass = require('gulp-ruby-sass');
3 | var browserSync = require('browser-sync');
4 | var reload = browserSync.reload;
5 | var include = require("gulp-include");
6 | var postcss = require('gulp-postcss');
7 | var autoprefixer = require('autoprefixer');
8 | var cssnano = require('cssnano');
9 | var rename = require('gulp-rename');
10 | var mqpacker = require("css-mqpacker");
11 | var imagemin = require('gulp-imagemin');
12 | var pngquant = require('imagemin-pngquant');
13 | var copy = require('gulp-copy');
14 | var ghPages = require('gulp-gh-pages');
15 | var colors = require('colors/safe');
16 | var del = require('del');
17 | var gulpSequence = require('gulp-sequence');
18 | var babel = require("gulp-babel");
19 | var sourcemaps = require("gulp-sourcemaps");
20 |
21 | // SASS, AUTOPREFIXR, MINIMIZE
22 | gulp.task('sass', function() {
23 | var processors = [
24 | autoprefixer({browsers: [
25 | 'last 1 version',
26 | 'last 2 Chrome versions',
27 | 'last 2 Firefox versions',
28 | 'last 2 Opera versions',
29 | 'last 2 Edge versions'
30 | ]}),
31 | mqpacker()
32 | ];
33 |
34 | console.log('⬤ Run ' + colors.yellow('Sass') +
35 | ' + ' +
36 | colors.green('Autoprefixer') +
37 | ' + ' +
38 | colors.cyan('Cssnano') + ' ⬤'
39 | );
40 |
41 | return sass('src/scss/styles.scss')
42 | .pipe(postcss(processors))
43 | .pipe(gulp.dest('assets/css'))
44 | .pipe(reload({ stream:true }))
45 | .pipe(postcss([cssnano()]))
46 | .pipe(rename('styles.min.css'))
47 | .pipe(gulp.dest('assets/css'));
48 | });
49 |
50 | // JS
51 | gulp.task('js', function () {
52 | return gulp.src('src/js/**/*.js')
53 | .pipe(sourcemaps.init())
54 | .pipe(babel())
55 | .pipe(sourcemaps.write("."))
56 | .pipe(gulp.dest('assets/js/'))
57 | .pipe(reload({ stream:true }));
58 | });
59 |
60 | // IMAGES
61 | gulp.task('images', function () {
62 | console.log(colors.magenta('⬤ Optimize images... ⬤'));
63 |
64 | return gulp.src('src/img/*')
65 | .pipe(imagemin({
66 | progressive: true,
67 | svgoPlugins: [{removeViewBox: false}],
68 | use: [pngquant()]
69 | }))
70 | .pipe(gulp.dest('assets/img'));
71 | });
72 |
73 | // INCLUDE BLOCKS IN HTML
74 | gulp.task('include', function() {
75 | console.log(colors.blue('⬤ Include files to HTML... ⬤'));
76 |
77 | gulp.src('src/index.html')
78 | .pipe(include())
79 | .on('error', console.log)
80 | .pipe(gulp.dest('.'))
81 | .pipe(reload({ stream:true }));
82 | });
83 |
84 | // WATCH SASS, PREPROCESS AND RELOAD
85 | gulp.task('serve', ['sass'], function() {
86 | browserSync({
87 | server: {
88 | baseDir: '.'
89 | }
90 | });
91 |
92 | gulp.watch(['src/**/*.scss'], ['sass']);
93 | gulp.watch(['src/**/*.html'], ['include']);
94 | gulp.watch(['src/**/*.js'], ['js']);
95 | });
96 |
97 | // CLEAN BUILD
98 | gulp.task('clean', function(){
99 | del(['build/**']).then(paths => {
100 | console.log('⬤ Deleted files and folders:\n', paths.join('\n'));
101 | });
102 | });
103 |
104 | // CLEAN BUILD & COPY FILES TO IT
105 | gulp.task('copy', function() {
106 | console.log(colors.magenta('⬤ Copy files to build/... ⬤'));
107 |
108 | return gulp.src(['assets/**/*', '*.html'])
109 | .pipe(copy('build/'));
110 | });
111 |
112 | gulp.task('build', function (cb) {
113 | gulpSequence('clean', ['js', 'sass', 'include'], 'copy', cb)
114 | })
115 |
116 | // PUBLISH TO GITHUB PAGES
117 | gulp.task('ghPages', function() {
118 | console.log(colors.rainbow('⬤ Publish to Github Pages... ⬤'));
119 |
120 | return gulp.src('build/**/*')
121 | .pipe(ghPages());
122 | });
123 |
124 | gulp.task('default', function() {
125 | console.log(colors.rainbow('⬤ ================================ ⬤\n'));
126 | console.log(' AVAILABLE COMMANDS:');
127 | console.log(' ' + colors.cyan('-------------------\n'));
128 | console.log(' ' + colors.yellow('npm start') +
129 | ' — run local server with watcher');
130 | console.log(' ' + colors.green('npm run build') +
131 | ' — make build of the project');
132 | console.log(' ' + colors.cyan('npm run deploy') +
133 | ' — make build and publish project to Github Pages');
134 | console.log(colors.rainbow('\n⬤ ================================ ⬤'));
135 | });
136 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wave-maker",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.html",
6 | "author": "yoksel",
7 | "license": "ISC",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/yoksel/wave-maker.git"
11 | },
12 | "devDependencies": {
13 | "autoprefixer": "^6.3.5",
14 | "babel-core": "^6.26.3",
15 | "babel-preset-env": "^1.7.0",
16 | "babel-preset-latest": "^6.24.1",
17 | "browser-sync": "^2.11.2",
18 | "colors": "^1.1.2",
19 | "css-mqpacker": "^4.0.1",
20 | "cssnano": "^3.5.2",
21 | "del": "^2.2.0",
22 | "editorconfig-tools": "^0.1.1",
23 | "gulp": "^3.9.1",
24 | "gulp-babel": "^7.0.1",
25 | "gulp-copy": "0.0.2",
26 | "gulp-gh-pages": "^0.5.4",
27 | "gulp-imagemin": "^2.4.0",
28 | "gulp-include": "^2.1.0",
29 | "gulp-postcss": "^6.1.0",
30 | "gulp-rename": "^1.2.2",
31 | "gulp-ruby-sass": "^2.0.6",
32 | "gulp-sequence": "^1.0.0",
33 | "gulp-sourcemaps": "^2.6.4",
34 | "imagemin-pngquant": "^4.2.2"
35 | },
36 | "scripts": {
37 | "start": "npm run build && gulp serve",
38 | "img": "gulp images",
39 | "build": "gulp build",
40 | "deploy": "npm run build && gulp ghPages",
41 | "check": "editorconfig-tools check *",
42 | "fix": "editorconfig-tools fix *"
43 | },
44 | "babel": {
45 | "presets": [
46 | "env"
47 | ]
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/blocks/footer.html:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/blocks/header.html:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wave Maker
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | <path d=""/>
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | flip the value for even
43 |
44 |
45 |
46 |
47 |
48 |
54 |
55 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/js/script.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var doc = document;
4 | var $ = tinyLib;
5 | var isCmdPressed = false;
6 |
7 | var svg = $.get('.svg');
8 | var targetPath = $.get('.shape-arc');
9 |
10 | var popup = $.get('.popup');
11 | var popupOpenedClass = 'popup--opened';
12 | var popupToggle = $.get('.popup__toggle');
13 |
14 | var code = $.get('.code');
15 | var codeOutput = $.get('.code__textarea');
16 | var codeButton = $.get('.code__toggle');
17 |
18 | var waveTypesItems = $.get('.wave-types__items');
19 |
20 | var pathCoordsAttrs = $.get('.path-coords__attrs');
21 | var attrsClass = 'attrs';
22 | var itemClass = attrsClass + '__item';
23 | var itemLineClass = itemClass + '--line';
24 | var inputClass = attrsClass + '__input';
25 | var inputErrorClass = inputClass + '--error';
26 | var labelClass = attrsClass + '__label';
27 | var labelLineClass = labelClass + '--line';
28 | var labelHiddenClass = labelClass + '--hidden';
29 | var errorClass = attrsClass + '__error';
30 |
31 | var pathCoordsList = [{
32 | prop: 'startLetter'
33 | },
34 | {
35 | prop: 'startX',
36 | desc: 'start X'
37 | },
38 | {
39 | prop: 'startY',
40 | desc: 'start Y'
41 | },
42 | {
43 | prop: 'arcLetter'
44 | },
45 | {
46 | prop: 'rX',
47 | desc: 'rx',
48 | min: 0
49 | },
50 | {
51 | prop: 'rY',
52 | desc: 'ry',
53 | min: 0
54 | },
55 | {
56 | prop: 'xRot',
57 | desc: 'x-axis-rotation',
58 | },
59 | {
60 | prop: 'largeArc',
61 | desc: 'large-arc-flag',
62 | min: 0,
63 | max: 1
64 | },
65 | {
66 | prop: 'sweep',
67 | desc: 'sweep-flag',
68 | min: 0,
69 | max: 1
70 | },
71 | {
72 | prop: 'endX',
73 | desc: 'end X'
74 | },
75 | {
76 | prop: 'endY',
77 | desc: 'end Y'
78 | },
79 | ];
80 |
81 | var pathParams = $.get('.path-params');
82 | var pathParamsList = [{
83 | prop: 'repeat',
84 | desc: 'repeat',
85 | min: 0
86 | },
87 | {
88 | prop: 'strokeWidth',
89 | desc: 'stroke-width',
90 | min: 1
91 | }
92 | ];
93 |
94 | var flags = $.get('.flags');
95 | var flagsList = [
96 | {
97 | prop: 'rotateLargeArc',
98 | desc: 'larg-arc-flag',
99 | type: 'checkbox',
100 | disableCond: {
101 | prop: 'repeat',
102 | value: 0
103 | }
104 | },
105 | {
106 | prop: 'rotateSweep',
107 | desc: 'sweep-flag',
108 | type: 'checkbox',
109 | disableCond: {
110 | prop: 'repeat',
111 | value: 0
112 | }
113 | }
114 | ];
115 |
116 | var inputsToDisable = [];
117 |
118 | var wavesInputsList = {
119 | radiowave: {
120 | startX: 150,
121 | startY: 200,
122 | rX: 80,
123 | rY: 100,
124 | endY: 200,
125 | endX: 300,
126 | xRot: 0,
127 | largeArc: 0,
128 | sweep: 0,
129 | repeat: 4,
130 | rotateSweep: true,
131 | rotateLargeArc: false,
132 | strokeWidthBtn: 18
133 | },
134 | seawave: {
135 | startX: 150,
136 | startY: 200,
137 | rX: 80,
138 | rY: 100,
139 | endX: 300,
140 | endY: 200,
141 | xRot: 0,
142 | largeArc: 0,
143 | sweep: 0,
144 | repeat: 4,
145 | repeatBtn: 2,
146 | rotateSweep: false,
147 | rotateLargeArc: false,
148 | strokeWidthBtn: 11
149 | },
150 | lightbulbs: {
151 | startX: 150,
152 | startY: 200,
153 | rX: 80,
154 | rY: 80,
155 | endX: 250,
156 | endY: 200,
157 | xRot: 0,
158 | largeArc: 1,
159 | sweep: 1,
160 | repeat: 6,
161 | rotateSweep: true,
162 | rotateLargeArc: false,
163 | strokeWidthBtn: 21
164 | },
165 | cursive: {
166 | startX: 150,
167 | startY: 200,
168 | rX: 20,
169 | rY: 90,
170 | endX: 300,
171 | endY: 200,
172 | xRot: 60,
173 | largeArc: 1,
174 | sweep: 0,
175 | repeat: 4,
176 | rotateSweep: true,
177 | rotateLargeArc: false,
178 | strokeWidthBtn: 20
179 | },
180 | bubbles: {
181 | startX: 150,
182 | startY: 220,
183 | rX: 80,
184 | rY: 80,
185 | endX: 230,
186 | endY: 130,
187 | xRot: 0,
188 | largeArc: 0,
189 | sweep: 0,
190 | repeat: 7,
191 | rotateSweep: true,
192 | rotateLargeArc: true,
193 | strokeWidthBtn: 16
194 | },
195 | leaves: {
196 | startX: 150,
197 | startY: 220,
198 | rX: 160,
199 | rY: 200,
200 | endX: 230,
201 | endY: 50,
202 | xRot: 0,
203 | largeArc: 0,
204 | sweep: 1,
205 | repeat: 7,
206 | rotateSweep: false,
207 | rotateLargeArc: false,
208 | strokeWidthBtn: 15
209 | },
210 | circle: {
211 | hidden: true,
212 | startX: 200,
213 | startY: 50,
214 | rX: 100,
215 | rY: 100,
216 | endX: 200,
217 | endY: 300,
218 | xRot: 0,
219 | largeArc: 0,
220 | sweep: 0,
221 | repeat: 1,
222 | rotateSweep: false,
223 | rotateLargeArc: true
224 | }
225 | };
226 |
227 | //---------------------------------------------
228 |
229 | var Arc = function (params) {
230 |
231 | this.params = params;
232 | this.path = params.path || targetPath;
233 | this.hasControls = params.hasControls || false;
234 | this.startLetter = 'M';
235 | this.arcLetter = 'A';
236 | this.startX = params.startX || 150;
237 | this.startY = params.startY || 200;
238 | this.endX = params.endX || 400;
239 | this.endY = params.endY || 200;
240 | this.rX = params.rX || 130;
241 | this.rY = params.rY || 120;
242 | this.xRot = params.xRot || 0;
243 | this.largeArc = params.largeArc || 0;
244 | this.sweep = params.sweep || 0;
245 | this.repeat = params.repeat || 0;
246 | this.strokeWidth = params.strokeWidth || 5;
247 | this.pathCoordsInputs = [];
248 |
249 | this.rotateSweep = params.rotateSweep !== undefined ? params.rotateSweep : true;
250 | this.rotateLargeArc = params.rotateLargeArc !== undefined ? params.rotateLargeArc : false;
251 |
252 | if (this.hasControls) {
253 | this.addHelpers();
254 | this.addControls();
255 |
256 | }
257 | this.getPathCoords();
258 | this.setPathCoords();
259 |
260 | if (this.hasControls) {
261 | this.addPathParams({
262 | list: pathCoordsList,
263 | target: pathCoordsAttrs,
264 | itemIsLine: false,
265 | labelIsHidden: true,
266 | });
267 | this.addPathParams({
268 | list: pathParamsList,
269 | target: pathParams,
270 | itemIsLine: true,
271 | labelIsHidden: false,
272 | });
273 | this.addPathParams({
274 | list: flagsList,
275 | target: flags,
276 | itemIsLine: true,
277 | labelIsHidden: false,
278 | });
279 |
280 | this.addWaveInputs();
281 | }
282 | };
283 |
284 | //---------------------------------------------
285 |
286 | Arc.prototype.getPathCoords = function () {
287 | this.pathCoordsSet = {
288 | startLetter: this.startLetter,
289 | startX: this.startX,
290 | startY: this.startY,
291 | arcLetter: this.arcLetter,
292 | rX: this.rX,
293 | rY: this.rY,
294 | xRot: this.xRot,
295 | largeArc: this.largeArc,
296 | sweep: this.sweep,
297 | endX: this.endX,
298 | endY: this.endY
299 | };
300 |
301 | for (var key in this.pathCoordsSet) {
302 | var item = this.pathCoordsSet[key];
303 | if (!isNaN(item)) {
304 | this.pathCoordsSet[key] = Math.round(item);
305 | }
306 | }
307 |
308 | var pathVals = Object.values(this.pathCoordsSet);
309 | this.pathCoords = pathVals.join(' ');
310 | };
311 |
312 | //---------------------------------------------
313 |
314 | Arc.prototype.setPathCoords = function () {
315 | this.path.attr({
316 | 'd': this.pathCoords,
317 | 'stroke-width': this.strokeWidth
318 | });
319 | this.path.rect = this.path.elem.getBBox();
320 |
321 | this.addWaves();
322 |
323 | if(this.hasControls) {
324 | this.setAllHelperArcParams();
325 | this.setAllControlsParams();
326 | this.updateCode();
327 | }
328 | };
329 |
330 | //---------------------------------------------
331 |
332 | Arc.prototype.addControls = function () {
333 | var that = this;
334 | this.controls = {
335 | start: {
336 | coordsFunc: this.getStartCoords,
337 | fill: 'greenyellow',
338 | targets: {
339 | 'startX': getMouseX,
340 | 'startY': getMouseY
341 | }
342 | },
343 | end: {
344 | coordsFunc: this.getEndCoords,
345 | fill: 'greenyellow',
346 | targets: {
347 | 'endX': getMouseX,
348 | 'endY': getMouseY
349 | }
350 | },
351 | // I dont't know how to place them on path
352 | // with rotation and if startY != endY
353 | // rx: {
354 | // coordsFunc: this.getRxCoords,
355 | // targets: {
356 | // 'rX': this.getRX
357 | // }
358 | // },
359 | // ry: {
360 | // coordsFunc: this.getRyCoords,
361 | // targets: {
362 | // 'rY': this.getRY
363 | // }
364 | // }
365 | };
366 |
367 | for (var key in this.controls) {
368 | var set = this.controls[key];
369 | set.elemSet = $.createNS('circle')
370 | .attr({
371 | id: key,
372 | r: 7
373 | })
374 | .addClass('point-control');
375 |
376 | svg.append(set.elemSet);
377 |
378 | set.elemSet.elem.addEventListener('mousedown', function (event) {
379 | that.currentControl = that.controls[this.id];
380 | doc.onmousemove = function (event) {
381 | that.drag(event);
382 | }
383 | });
384 | }
385 | };
386 |
387 | //---------------------------------------------
388 |
389 | Arc.prototype.getStartCoords = function () {
390 | return {
391 | cx: this.startX ? this.startX : 0,
392 | cy: this.startY ? this.startY : 0
393 | }
394 | };
395 |
396 | //---------------------------------------------
397 |
398 | Arc.prototype.getEndCoords = function () {
399 | return {
400 | cx: this.endX ? this.endX : 0,
401 | cy: this.endY ? this.endY : 0
402 | }
403 | };
404 |
405 | //---------------------------------------------
406 |
407 | Arc.prototype.getRxCoords = function () {
408 | return {
409 | cx: (this.arcHelpers.flipBoth.rect.x + this.arcHelpers.flipBoth.rect.width),
410 | cy: (this.arcHelpers.flipBoth.rect.y + this.rY)
411 | }
412 | };
413 |
414 | Arc.prototype.getRX = function (event) {
415 | var rect = this.arcHelpers.flipBoth.rect;
416 | var offset = event.offsetX - (rect.x + rect.width);
417 | var rX = this.rX + offset;
418 | return rX;
419 | };
420 |
421 | //---------------------------------------------
422 |
423 | Arc.prototype.getRyCoords = function () {
424 | return {
425 | cx: (this.path.rect.x + this.path.rect.width / 2),
426 | cy: (this.path.rect.y + this.path.rect.height)
427 | }
428 | };
429 |
430 | Arc.prototype.getRY = function (event) {
431 | var rect = this.path.rect;
432 | var offset = event.offsetY - (rect.y + rect.height);
433 | var rY = this.rY + offset;
434 | return rY;
435 | }
436 |
437 | //---------------------------------------------
438 |
439 | Arc.prototype.setAllControlsParams = function () {
440 | for (var key in this.controls) {
441 | var set = this.controls[key];
442 | var coords = set.coordsFunc.apply(this);
443 |
444 | set.elemSet.attr({
445 | cx: coords.cx,
446 | cy: coords.cy,
447 | });
448 | }
449 | };
450 |
451 | //---------------------------------------------
452 |
453 | Arc.prototype.drag = function (event) {
454 | var x = event.offsetX;
455 | var y = event.offsetY;
456 | var targets = this.currentControl.targets;
457 |
458 | for (var target in targets) {
459 | this[target] = targets[target].call(this, event);
460 | }
461 |
462 | this.getPathCoords();
463 | this.setPathCoords();
464 | this.updateInputs();
465 |
466 | doc.onmouseup = function () {
467 | doc.onmousemove = null;
468 | this.currentControl = null;
469 | };
470 | };
471 |
472 | //---------------------------------------------
473 |
474 | Arc.prototype.addHelpers = function () {
475 | this.arcHelpers = {};
476 | this.arcHelpers.flipBoth = this.addHelperArc({
477 | id: 'arcHelper-flipSweepAndArc',
478 | flipSweep: true,
479 | flipLargeArc: true,
480 | // stroke: 'seagreen'
481 | });
482 | this.arcHelpers.flipArc = this.addHelperArc({
483 | id: 'arcHelper-flipArc',
484 | flipSweep: false,
485 | flipLargeArc: true,
486 | // stroke: 'orangered'
487 | });
488 | this.arcHelpers.flipSweep = this.addHelperArc({
489 | id: 'arcHelper-flipSweep',
490 | flipSweep: true,
491 | flipLargeArc: false,
492 | // stroke: 'royalblue'
493 | });
494 |
495 | this.arcHelpers.list = [
496 | this.arcHelpers.flipBoth,
497 | this.arcHelpers.flipArc,
498 | this.arcHelpers.flipSweep
499 | ];
500 | };
501 |
502 | //---------------------------------------------
503 |
504 | Arc.prototype.addHelperArc = function (params) {
505 | var arcHelper = {};
506 | arcHelper.params = params;
507 | arcHelper.elemSet = $.createNS('path')
508 | .attr({
509 | 'id': params.id,
510 | 'fill': 'none',
511 | 'stroke': params.stroke || '#999'
512 | });
513 |
514 | svg.prepend(arcHelper.elemSet);
515 | return arcHelper;
516 | };
517 |
518 | //---------------------------------------------
519 |
520 | Arc.prototype.setHelperArcParams = function (arcHelper) {
521 | var arcParamsSet = Object.assign({}, this.pathCoordsSet);
522 |
523 | if (arcHelper.params) {
524 | if (arcHelper.params.flipLargeArc) {
525 | arcParamsSet.largeArc = +!arcParamsSet.largeArc;
526 | }
527 | if (arcHelper.params.flipSweep) {
528 | arcParamsSet.sweep = +!arcParamsSet.sweep;
529 | }
530 | }
531 |
532 | var arcParams = Object.values(arcParamsSet).join(' ');
533 |
534 | arcHelper.elemSet.attr({
535 | 'd': arcParams
536 | });
537 | arcHelper.rect = arcHelper.elemSet.elem.getBBox();
538 | };
539 |
540 | //---------------------------------------------
541 |
542 | Arc.prototype.setAllHelperArcParams = function () {
543 | var that = this;
544 | this.arcHelpers.list.map(function (item) {
545 | that.setHelperArcParams(item);
546 | });
547 | };
548 |
549 | //---------------------------------------------
550 |
551 | Arc.prototype.changeValueByKeyboard = function (event, input, error) {
552 | if (!(event.keyCode == 38 || event.keyCode == 40)) {
553 | return;
554 | }
555 |
556 | var step = 1;
557 |
558 | if (event.shiftKey && (event.ctrlKey || isCmdPressed)) {
559 | step = 1000;
560 | } else if (event.ctrlKey || isCmdPressed) {
561 | step = 100;
562 | } else if (event.shiftKey) {
563 | step = 10;
564 | }
565 |
566 | if (event.keyCode === 38) {
567 | input.value = +input.value + step;
568 | } else {
569 | input.value = +input.value - step;
570 | }
571 |
572 | setInputWidth.apply(input);
573 |
574 | if (!checkValue.apply(input, [error])) {
575 | return false;
576 | }
577 |
578 | this[input.name] = input.value;
579 | this.getPathCoords();
580 | this.setPathCoords();
581 | };
582 |
583 | //---------------------------------------------
584 |
585 | Arc.prototype.updateInputs = function () {
586 | var that = this;
587 |
588 | this.pathCoordsInputs.forEach(function (item) {
589 | var name = item.elem.name;
590 | if (that[name] == null) {
591 | return;
592 | }
593 | var value = +item.elem.value;
594 | var newValue = that.pathCoordsSet[name] || that[name];
595 |
596 | if (item.elem.type === 'checkbox') {
597 | item.elem.checked = !!that[name];
598 | return;
599 | }
600 |
601 | item.elem.value = newValue;
602 |
603 | if (value !== newValue) {
604 | setInputWidth.apply(item.elem);
605 | }
606 |
607 | disableInputs.call(item.elem);
608 | });
609 | };
610 |
611 | //---------------------------------------------
612 |
613 | Arc.prototype.createInput = function (item) {
614 | var name = item.prop;
615 | var value = this[name];
616 |
617 | var input = $.create('input')
618 | .attr({
619 | type: item.type || 'text',
620 | name: name,
621 | id: name,
622 | value: value
623 | })
624 | .addClass([
625 | inputClass,
626 | inputClass + '--' + name,
627 | inputClass + '--' + typeof (value)
628 | ]);
629 |
630 | if (item.min !== undefined && item.min !== null) {
631 | input.attr({
632 | min: item.min
633 | });
634 | }
635 | if (item.max) {
636 | input.attr({
637 | max: item.max
638 | });
639 | }
640 | if (typeof (value) === 'string') {
641 | input.attr({
642 | 'disabled': ''
643 | });
644 | }
645 | else if (typeof (value) === 'boolean' && value === true) {
646 | input.attr({
647 | checked: value
648 | });
649 | }
650 |
651 |
652 | if (item.disableCond) {
653 | var cond = item.disableCond;
654 |
655 | if (this[cond.prop] === cond.value) {
656 | input.elem.disabled = true;
657 | }
658 |
659 | inputsToDisable.push({
660 | input: input,
661 | disableCond: item.disableCond
662 | })
663 | }
664 |
665 | return input;
666 | };
667 |
668 | //---------------------------------------------
669 |
670 | Arc.prototype.createLabel = function (item, params) {
671 | var name = item.prop;
672 | var value = this[name];
673 |
674 | var label = $.create('label')
675 | .attr({
676 | for: name
677 | })
678 | .addClass(labelClass);
679 |
680 | if (params.labelIsHidden) {
681 | label.addClass(labelHiddenClass);
682 | }
683 | if (params.itemIsLine) {
684 | label.addClass(labelLineClass);
685 | }
686 | label.html(item.desc);
687 |
688 | return label;
689 | };
690 |
691 | //---------------------------------------------
692 |
693 | Arc.prototype.createError = function (item) {
694 | if (item.min === undefined && item.max === undefined) {
695 | return null;
696 | }
697 | var error = $.create('span')
698 | .addClass(errorClass);
699 |
700 | return error;
701 | };
702 |
703 | //---------------------------------------------
704 |
705 | Arc.prototype.addPathParams = function (params) {
706 | var that = this;
707 | var list = params.list;
708 | var target = params.target;
709 | var items = [];
710 |
711 | list.forEach(function (item) {
712 | var name = item.prop;
713 | var value = that[name];
714 |
715 | var input = that.createInput(item);
716 |
717 | var label = that.createLabel(item, params);
718 |
719 | var error = that.createError(item);
720 |
721 | var item = $.create('span')
722 | .addClass([
723 | itemClass,
724 | itemClass + '--' + name
725 | ])
726 | .append([input, label, error]);
727 |
728 | if (params.itemIsLine) {
729 | item.addClass(itemLineClass);
730 | }
731 |
732 | that.pathCoordsInputs.push(input);
733 | items.push(item);
734 |
735 | // Events
736 | input.elem.addEventListener('input', function () {
737 | if (this.type === 'checkbox') {
738 | return;
739 | }
740 | setInputWidth.apply(this);
741 | if (!checkValue.apply(this, [error])) {
742 | return false;
743 | }
744 |
745 | that[this.name] = this.value;
746 | that.getPathCoords();
747 | that.setPathCoords();
748 | disableInputs.call(this);
749 | });
750 |
751 | input.elem.addEventListener('keydown', function (event) {
752 | if (this.type !== 'text') {
753 | return;
754 | }
755 |
756 | setIsCmd(event);
757 | that.changeValueByKeyboard(event, this, error);
758 | disableInputs.call(this);
759 | });
760 |
761 | input.elem.addEventListener('keyup', function (event) {
762 | unSetIsCmd(event);
763 | });
764 |
765 | input.elem.addEventListener('click', function (event) {
766 | if (this.type != 'checkbox') {
767 | return;
768 | }
769 |
770 | that[this.name] = this.checked;
771 |
772 | that.getPathCoords();
773 | that.setPathCoords();
774 | });
775 |
776 | });
777 |
778 | target.append(items);
779 | };
780 |
781 | //---------------------------------------------
782 |
783 | // context: input
784 | function disableInputs () {
785 | var inputId = this.id;
786 | var inputValue = +this.value;
787 |
788 | inputsToDisable.forEach(function (item) {
789 | var input = item.input;
790 | var cond = item.disableCond;
791 |
792 | if (inputId === cond.prop) {
793 | if (inputValue === cond.value) {
794 | input.elem.disabled = true;
795 | }
796 | else {
797 | input.elem.disabled = false;
798 | }
799 | }
800 | });
801 | };
802 |
803 | //---------------------------------------------
804 |
805 | Arc.prototype.addWaves = function () {
806 | var wavesParamsSet = [this.pathCoords];
807 | if (this.repeat === 0) {
808 | return;
809 | }
810 |
811 | for (var i = 0; i < this.repeat; i++) {
812 | wavesParamsSet.push(this.addWave(i));
813 | }
814 |
815 | var wavesParams = wavesParamsSet.join(' ');
816 | this.path.attr({
817 | 'd': wavesParams
818 | });
819 | };
820 |
821 | //---------------------------------------------
822 |
823 | Arc.prototype.addWave = function (counter) {
824 | var arcParamsSet = {};
825 | var waveWidth = this.pathCoordsSet.endX - this.pathCoordsSet.startX;
826 |
827 | for (var key in this.pathCoordsSet) {
828 | arcParamsSet[key] = this.pathCoordsSet[key];
829 | }
830 |
831 | delete arcParamsSet['startLetter'];
832 | delete arcParamsSet['startX'];
833 | delete arcParamsSet['startY'];
834 |
835 | arcParamsSet['endX'] = this.pathCoordsSet.endX + (waveWidth * (counter + 1));
836 | if (counter % 2 === 0) {
837 | if (this.rotateSweep) {
838 | arcParamsSet['sweep'] = +!this.pathCoordsSet.sweep;
839 | }
840 | if (this.rotateLargeArc) {
841 | arcParamsSet['largeArc'] = +!this.pathCoordsSet.largeArc;
842 | }
843 |
844 | arcParamsSet['endY'] = this.pathCoordsSet.startY;
845 | }
846 |
847 | var arcParamsVals = Object.values(arcParamsSet);
848 | var arcParams = arcParamsVals.join(' ');
849 | return arcParams;
850 | };
851 |
852 | //---------------------------------------------
853 |
854 | Arc.prototype.cloneParams = function () {
855 | var params = Object.assign({}, this.pathCoordsSet);
856 | params.repeat = this.repeat;
857 | params.rotateLargeArc = this.rotateLargeArc;
858 | params.rotateSweep = this.rotateSweep;
859 | params.strokeWidth = this.strokeWidth;
860 |
861 | return params;
862 | };
863 |
864 | //---------------------------------------------
865 |
866 | function getViewBoxByPath(path) {
867 | const bbBox = path.elem.getBBox();
868 | const strokeWidth = +path.attr('stroke-width');
869 | const width = bbBox.width;
870 | const height = bbBox.height;
871 |
872 | var viewBox = [
873 | bbBox.x - strokeWidth,
874 | bbBox.y - strokeWidth,
875 | width + strokeWidth * 2,
876 | height + strokeWidth * 2
877 | ];
878 |
879 | viewBox = viewBox.map(function (item) {
880 | return Math.round(item);
881 | });
882 | viewBox = viewBox.join(' ');
883 |
884 | return viewBox;
885 | }
886 |
887 | //---------------------------------------------
888 |
889 | Arc.prototype.getCode = function (params) {
890 | var params = params;
891 | var newParams = Object.assign({}, params);
892 | newParams.path = this.path.clone()
893 |
894 | if (newParams.strokeWidthBtn) {
895 | newParams.strokeWidth = newParams.strokeWidthBtn;
896 | }
897 | if (newParams.repeatBtn) {
898 | newParams.repeat = newParams.repeatBtn;
899 | }
900 |
901 | var newArc = new Arc(newParams);
902 | var newPath = newArc.path;
903 | var newPathElem = newPath.elem;
904 | newPathElem.removeAttribute('class');
905 |
906 | var copyRect = newPathElem.getBBox();
907 | var strokeWidth = +newArc.strokeWidth;
908 | var strokeWidthHalf = strokeWidth / 2;
909 |
910 | newArc.startX -= copyRect.x - strokeWidthHalf;
911 | newArc.startY -= copyRect.y - strokeWidthHalf;
912 | newArc.endX -= copyRect.x - strokeWidthHalf;
913 | newArc.endY -= copyRect.y - strokeWidthHalf;
914 |
915 | newArc.getPathCoords();
916 | newArc.setPathCoords();
917 |
918 | var viewBox = getViewBoxByPath(this.path);
919 |
920 | var result = ``;
921 | return result;
922 | };
923 |
924 | //---------------------------------------------
925 |
926 | Arc.prototype.updateCode = function () {
927 | var output = this.getCode(this.cloneParams());
928 | codeOutput.val(output);
929 |
930 | changeContentHeight.call(codeButton.elem);
931 | };
932 |
933 | //---------------------------------------------
934 |
935 | Arc.prototype.addWaveInputs = function () {
936 | var that = this;
937 | var prefix = 'wave-types';
938 | var items = [];
939 |
940 | for (var key in wavesInputsList) {
941 | var params = wavesInputsList[key];
942 |
943 | if (params.hidden) {
944 | continue;
945 | }
946 | var demoPath = this.getCode(params);
947 |
948 | var button = $.create('button')
949 | .attr({
950 | type: 'button',
951 | name: prefix,
952 | id: key
953 | })
954 | .html(demoPath)
955 | .addClass(prefix + '__button');
956 |
957 | var item = $.create('div')
958 | .addClass(prefix + '__item')
959 | .append([button]);
960 |
961 | items.push(item);
962 |
963 | button.elem.addEventListener('click', function () {
964 | var params = wavesInputsList[this.id];
965 |
966 | for (var key in params) {
967 | that[key] = params[key];
968 | }
969 |
970 | that.getPathCoords();
971 | that.setPathCoords();
972 | that.updateInputs();
973 | });
974 | }
975 |
976 | waveTypesItems.append(items);
977 |
978 | // Fix viewBox on examples
979 | items.forEach(item => {
980 | const svg = $.get('svg', item.elem);
981 | const path = $.get('path', item.elem);
982 | const viewBox = getViewBoxByPath(path);
983 |
984 | svg.attr('viewBox', viewBox);
985 | })
986 | };
987 |
988 | //---------------------------------------------
989 |
990 | function setInputWidth() {
991 | if (this.type !== 'text') {
992 | return;
993 | }
994 |
995 | this.style.minWidth = this.value.length * .65 + 'em';
996 | }
997 |
998 | //---------------------------------------------
999 |
1000 | function checkValue(errorElem) {
1001 | if (!errorElem) {
1002 | return true;
1003 | }
1004 |
1005 | errorElem.html('');
1006 | this.classList.remove(inputErrorClass);
1007 |
1008 | if (isNaN(this.value)) {
1009 | errorElem.html('not a number');
1010 | this.classList.add(inputErrorClass);
1011 | return false;
1012 | } else if (this.min && this.value < this.min) {
1013 | this.classList.add(inputErrorClass);
1014 | errorElem.html('minimum: ' + this.min);
1015 | return false;
1016 | } else if (this.max && this.value > this.max) {
1017 | this.classList.add(inputErrorClass);
1018 | errorElem.html('maximum: ' + this.max);
1019 | return false;
1020 | }
1021 |
1022 | return true;
1023 | }
1024 |
1025 | //---------------------------------------------
1026 |
1027 | function setIsCmd(event) {
1028 | // Chrome || FF
1029 | if (event.keyCode == 91 || (event.key === 'Meta' && event.keyCode === 224)) {
1030 | isCmdPressed = true;
1031 | }
1032 | }
1033 |
1034 | function unSetIsCmd(event) {
1035 | // Chrome || FF
1036 | if (event.keyCode == 91 || (event.key === 'Meta' && event.keyCode === 224)) {
1037 | isCmdPressed = false;
1038 | }
1039 | }
1040 | doc.addEventListener('keyup', function (event) {
1041 | unSetIsCmd(event);
1042 | });
1043 |
1044 | //---------------------------------------------
1045 |
1046 | function getMouseX(event) {
1047 | return event.offsetX;
1048 | }
1049 |
1050 | function getMouseY(event) {
1051 | return event.offsetY;
1052 | }
1053 |
1054 | //---------------------------------------------
1055 |
1056 | // Popup events
1057 | popup.forEach(function (item) {
1058 | item.elem.addEventListener('click', function (event) {
1059 | event.stopPropagation();
1060 | });
1061 | });
1062 |
1063 | popupToggle.forEach(function (item) {
1064 |
1065 | item.elem.addEventListener('click', function (event) {
1066 | var parent = this.parentNode;
1067 |
1068 | if (parent.classList.contains(popupOpenedClass)) {
1069 | parent.classList.remove(popupOpenedClass);
1070 | }
1071 | else {
1072 | closeOpened();
1073 | changeContentHeight.call(this);
1074 |
1075 | parent.classList.toggle(popupOpenedClass);
1076 | }
1077 | });
1078 | });
1079 |
1080 |
1081 | doc.addEventListener('click', function () {
1082 | closeOpened();
1083 | });
1084 |
1085 | function closeOpened() {
1086 | var popupPanel = $.get('.' + popupOpenedClass);
1087 |
1088 | if (popupPanel.elem) {
1089 | popupPanel.removeClass(popupOpenedClass);
1090 | }
1091 |
1092 | }
1093 |
1094 | function changeContentHeight() {
1095 | var parent = this.parentNode;
1096 | var container = parent.querySelector('.popup__container');
1097 | var content = parent.querySelector('.popup__content');
1098 |
1099 | // trick to get real scrollHeight
1100 | content.style.maxHeight = '0';
1101 | container.style.maxHeight = (content.scrollHeight + 10) + 'px';
1102 | content.style.maxHeight = null;
1103 | }
1104 |
1105 | //---------------------------------------------
1106 |
1107 | var arc = new Arc({
1108 | path: targetPath,
1109 | hasControls: true
1110 | });
1111 |
--------------------------------------------------------------------------------
/src/js/tinylib.js:
--------------------------------------------------------------------------------
1 | // Tiny library
2 |
3 | //---------------------------------
4 | // http://checkman.io/blog/creating-a-javascript-library/
5 | // http://code.tutsplus.com/tutorials/build-your-first-javascript-library--net-26796
6 | //http://lea.verou.me/2015/04/idea-extending-native-dom-prototypes-without-collisions/
7 |
8 | (function (window) {
9 |
10 | 'use strict';
11 |
12 | function define_library() {
13 |
14 | var tinyLib = {};
15 | var doc = document;
16 | var ns = 'http://www.w3.org/2000/svg';
17 |
18 | function ElemSet(elem) {
19 | this.elem = elem;
20 | }
21 |
22 | //---------------------------------
23 |
24 | tinyLib.get = function (selector, context) {
25 |
26 | var contextElem = context ? context : doc;
27 | if (context && context.elem) {
28 | contextElem = context.elem;
29 | }
30 |
31 | var nodeList = contextElem.querySelectorAll(selector);
32 | var elemsArr = Array.prototype.slice.call(nodeList);
33 |
34 | var elemsList = elemsArr.map(function (item) {
35 | return new ElemSet(item);
36 | });
37 |
38 | if (elemsList.length === 1) {
39 | return elemsList[0];
40 | }
41 |
42 | return elemsList;
43 | };
44 |
45 | //---------------------------------
46 |
47 | tinyLib.create = function (tagName) {
48 | var elem = doc.createElement(tagName);
49 | return new ElemSet(elem);
50 | };
51 |
52 | //---------------------------------
53 |
54 | tinyLib.createNS = function (tagName) {
55 | var elem = doc.createElementNS(ns, tagName);
56 | return new ElemSet(elem);
57 | };
58 |
59 | //------------------------------
60 | // ElemSet Methods
61 |
62 | ElemSet.prototype.addClass = function (classNames) {
63 | var elem = this.elem;
64 |
65 | if (typeof classNames === 'string') {
66 | classNames = [classNames];
67 | }
68 |
69 | classNames.forEach(function (className) {
70 | elem.classList.add(className);
71 | });
72 |
73 | return this;
74 | };
75 |
76 | //---------------------------------
77 |
78 | ElemSet.prototype.removeClass = function (classNames) {
79 |
80 | var elem = this.elem;
81 |
82 | if (typeof classNames === 'string') {
83 | classNames = [classNames];
84 | }
85 |
86 | classNames.forEach(function (className) {
87 | elem.classList.remove(className);
88 | });
89 |
90 | return this;
91 | };
92 |
93 | //---------------------------------
94 |
95 | ElemSet.prototype.toggleClass = function (className) {
96 |
97 | var elem = this.elem;
98 | elem.classList.toggle(className);
99 |
100 | return this;
101 | };
102 |
103 | //---------------------------------
104 |
105 | ElemSet.prototype.append = function (elem) {
106 | var itemsToAdd = inputElemToItemsList(elem);
107 | var that = this;
108 |
109 | itemsToAdd.forEach(function (item) {
110 | if (!item) {
111 | return;
112 | }
113 | that.elem.appendChild(item.elem);
114 | });
115 |
116 | return this;
117 | };
118 |
119 | //---------------------------------
120 |
121 | ElemSet.prototype.prepend = function (elem) {
122 | var itemsToAdd = inputElemToItemsList(elem);
123 | var that = this;
124 |
125 | itemsToAdd.forEach(function (item) {
126 | if (!item) {
127 | return;
128 | }
129 | that.elem.insertBefore(item.elem, that.elem.firstChild);
130 | });
131 |
132 | return this;
133 | };
134 |
135 | //---------------------------------
136 |
137 | ElemSet.prototype.clone = function () {
138 | var elemToClone = this.elem;
139 | var clonedElem = elemToClone.cloneNode(true);
140 |
141 | return new ElemSet(clonedElem);
142 | };
143 |
144 | //---------------------------------
145 |
146 | ElemSet.prototype.attr = function (attrName, attrVal) {
147 |
148 | var elem = this.elem;
149 | var attrSet = {};
150 |
151 | if (attrVal) {
152 | attrSet[attrName] = attrVal;
153 | } else if (typeof attrName === 'object') {
154 | attrSet = attrName;
155 | }
156 |
157 | if (Object.keys(attrSet).length > 0) {
158 | for (var key in attrSet) {
159 | elem.setAttribute(key, attrSet[key]);
160 | }
161 | return this;
162 | }
163 |
164 | var out = elem.getAttribute(attrName);
165 | return out;
166 |
167 | };
168 |
169 | //---------------------------------
170 |
171 | ElemSet.prototype.val = function (content) {
172 | if (!content) {
173 | return this.elem.value;
174 | }
175 |
176 | this.elem.value = content;
177 | return this;
178 | };
179 |
180 | //---------------------------------
181 |
182 | ElemSet.prototype.html = function (content) {
183 | var elem = this.elem;
184 |
185 | if (content) {
186 | elem.innerHTML = content;
187 | return this;
188 | }
189 |
190 | return this;
191 | };
192 |
193 | //---------------------------------
194 |
195 | ElemSet.prototype.data = function (content) {
196 | var elem = this.elem;
197 |
198 | if (content) {
199 | // Input: list
200 | if (Array.isArray(content) === true) {
201 | var dataList = {};
202 |
203 | content.forEach(function (item) {
204 | var data = elem.dataset[item];
205 | if (data) {
206 | dataList[item] = data;
207 | }
208 | });
209 |
210 | return dataList;
211 | }
212 | // Input: object
213 | else if (typeof content === 'object') {
214 |
215 | for (var key in content) {
216 | elem.dataset[key] = content[key];
217 | }
218 |
219 | return elem.dataset;
220 | }
221 | // Input: string
222 | else if (typeof content === 'string') {
223 | var data = elem.dataset[content];
224 | return data;
225 | }
226 |
227 | }
228 |
229 | return null;
230 | };
231 |
232 | //---------------------------------
233 | // Colored console output
234 |
235 | var consoleStyles = {
236 | 'h1': 'font: 2.5em/1 Arial; color: crimson;',
237 | 'h2': 'font: 2em/1 Arial; color: orangered;',
238 | 'h3': 'font: 1.6em/1 Arial; color: olivedrab;',
239 | 'h4': 'font: bold 1.3em/1 Arial; color: midnightblue',
240 | 'warn': 'padding: 0 .3rem; background: crimson; font: 2em/1 Arial; color: white'
241 | };
242 |
243 | tinyLib.out = function (msg, style) {
244 | if (!style || !consoleStyles[style]) {
245 | style = '';
246 | }
247 | console.log('%c' + msg, consoleStyles[style]);
248 | };
249 |
250 | tinyLib.dir = function (msg) {
251 | console.dir(msg);
252 | };
253 |
254 | //---------------------------------
255 |
256 | function inputElemToItemsList(elem) {
257 | var elemToAdd = elem;
258 | var itemsToAdd = [];
259 |
260 | if (typeof elem === 'string') {
261 | elemToAdd = tinyLib.create(elem);
262 | itemsToAdd.push(elemToAdd);
263 | } else if (Array.isArray(elem) === true) {
264 | itemsToAdd = elem;
265 | } else {
266 | itemsToAdd.push(elemToAdd);
267 | }
268 |
269 | return itemsToAdd;
270 | }
271 |
272 | //---------------------------------
273 |
274 | return tinyLib;
275 | }
276 |
277 | //---------------------------------
278 |
279 | if (typeof tinyLib === 'undefined') {
280 | window.tinyLib = define_library();
281 | }
282 |
283 | })(window);
284 |
--------------------------------------------------------------------------------
/src/scss/_base.scss:
--------------------------------------------------------------------------------
1 | HTML,
2 | BODY {
3 | height: 100%;
4 | }
5 |
6 | BODY {
7 | min-width: 600px;
8 | display: flex;
9 | background: #FFF;
10 | font: #{$font-size}/1.4 $font-family;
11 | color: $color-font;
12 | }
13 |
14 | A {
15 | color: $color-link;
16 | &:visited {
17 | color: $color-link-visited;
18 | }
19 | &:hover {
20 | color: $color-link-hover;
21 | }
22 | }
23 |
24 | UL {
25 | margin: 0;
26 | padding: 0;
27 | list-style: none;
28 | }
29 |
30 | H1,
31 | H2,
32 | H3,
33 | H4,
34 | H5 {
35 | font-weight: normal;
36 | }
37 |
38 |
39 | /* LAYOUT */
40 |
41 | .header,
42 | .container,
43 | .footer {
44 | @include container-width;
45 | margin: 0;
46 | padding: 0;
47 | }
48 |
49 | .container {
50 | display: flex;
51 | @media ( max-width: $bp-tablet) {
52 | flex-direction: column;
53 | }
54 | }
55 |
56 | .main {
57 | display: flex;
58 | flex-direction: column;
59 | flex-grow: 1;
60 | }
61 |
62 | .aside {
63 | flex-basis: 200px;
64 | margin-left: 30px;
65 | @media ( max-width: $bp-tablet) {
66 | flex-basis: 0;
67 | margin-left: 0;
68 | margin-top: 1rem;
69 | }
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/src/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin container-width {
2 | width: 100%;
3 | min-width: $bp-mobile;
4 |
5 | @media (min-width: $bp-tablet ) {
6 | min-width: $bp-tablet;
7 | }
8 |
9 | @media (min-width: $bp-desktop ) {
10 | min-width: $bp-desktop;
11 | }
12 | }
13 |
14 | @mixin clear {
15 | &:before,
16 | &:after {
17 | content: "";
18 | display: table;
19 | clear: both;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/scss/_normalize.scss:
--------------------------------------------------------------------------------
1 | /*! normalize.css v4.0.0 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /**
4 | * 1. Change the default font family in all browsers (opinionated).
5 | * 2. Prevent adjustments of font size after orientation changes in IE and iOS.
6 | */
7 |
8 | html {
9 | font-family: sans-serif; /* 1 */
10 | -ms-text-size-adjust: 100%; /* 2 */
11 | -webkit-text-size-adjust: 100%; /* 2 */
12 | }
13 |
14 | /**
15 | * Remove the margin in all browsers (opinionated).
16 | */
17 |
18 | body {
19 | margin: 0;
20 | }
21 |
22 | /* HTML5 display definitions
23 | ========================================================================== */
24 |
25 | /**
26 | * Add the correct display in IE 9-.
27 | * 1. Add the correct display in Edge, IE, and Firefox.
28 | * 2. Add the correct display in IE.
29 | */
30 |
31 | article,
32 | aside,
33 | details, /* 1 */
34 | figcaption,
35 | figure,
36 | footer,
37 | header,
38 | main, /* 2 */
39 | menu,
40 | nav,
41 | section,
42 | summary { /* 1 */
43 | display: block;
44 | }
45 |
46 | /**
47 | * Add the correct display in IE 9-.
48 | */
49 |
50 | audio,
51 | canvas,
52 | progress,
53 | video {
54 | display: inline-block;
55 | }
56 |
57 | /**
58 | * Add the correct display in iOS 4-7.
59 | */
60 |
61 | audio:not([controls]) {
62 | display: none;
63 | height: 0;
64 | }
65 |
66 | /**
67 | * Add the correct vertical alignment in Chrome, Firefox, and Opera.
68 | */
69 |
70 | progress {
71 | vertical-align: baseline;
72 | }
73 |
74 | /**
75 | * Add the correct display in IE 10-.
76 | * 1. Add the correct display in IE.
77 | */
78 |
79 | template, /* 1 */
80 | [hidden] {
81 | display: none;
82 | }
83 |
84 | /* Links
85 | ========================================================================== */
86 |
87 | /**
88 | * Remove the gray background on active links in IE 10.
89 | */
90 |
91 | a {
92 | background-color: transparent;
93 | }
94 |
95 | /**
96 | * Remove the outline on focused links when they are also active or hovered
97 | * in all browsers (opinionated).
98 | */
99 |
100 | a:active,
101 | a:hover {
102 | outline-width: 0;
103 | }
104 |
105 | /* Text-level semantics
106 | ========================================================================== */
107 |
108 | /**
109 | * 1. Remove the bottom border in Firefox 39-.
110 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
111 | */
112 |
113 | abbr[title] {
114 | border-bottom: none; /* 1 */
115 | text-decoration: underline; /* 2 */
116 | text-decoration: underline dotted; /* 2 */
117 | }
118 |
119 | /**
120 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
121 | */
122 |
123 | b,
124 | strong {
125 | font-weight: inherit;
126 | }
127 |
128 | /**
129 | * Add the correct font weight in Chrome, Edge, and Safari.
130 | */
131 |
132 | b,
133 | strong {
134 | font-weight: bolder;
135 | }
136 |
137 | /**
138 | * Add the correct font style in Android 4.3-.
139 | */
140 |
141 | dfn {
142 | font-style: italic;
143 | }
144 |
145 | /**
146 | * Correct the font size and margin on `h1` elements within `section` and
147 | * `article` contexts in Chrome, Firefox, and Safari.
148 | */
149 |
150 | h1 {
151 | font-size: 2em;
152 | margin: 0.67em 0;
153 | }
154 |
155 | /**
156 | * Add the correct background and color in IE 9-.
157 | */
158 |
159 | mark {
160 | background-color: #ff0;
161 | color: #000;
162 | }
163 |
164 | /**
165 | * Add the correct font size in all browsers.
166 | */
167 |
168 | small {
169 | font-size: 80%;
170 | }
171 |
172 | /**
173 | * Prevent `sub` and `sup` elements from affecting the line height in
174 | * all browsers.
175 | */
176 |
177 | sub,
178 | sup {
179 | font-size: 75%;
180 | line-height: 0;
181 | position: relative;
182 | vertical-align: baseline;
183 | }
184 |
185 | sub {
186 | bottom: -0.25em;
187 | }
188 |
189 | sup {
190 | top: -0.5em;
191 | }
192 |
193 | /* Embedded content
194 | ========================================================================== */
195 |
196 | /**
197 | * Remove the border on images inside links in IE 10-.
198 | */
199 |
200 | img {
201 | border-style: none;
202 | }
203 |
204 | /**
205 | * Hide the overflow in IE.
206 | */
207 |
208 | svg:not(:root) {
209 | overflow: hidden;
210 | }
211 |
212 | /* Grouping content
213 | ========================================================================== */
214 |
215 | /**
216 | * 1. Correct the inheritance and scaling of font size in all browsers.
217 | * 2. Correct the odd `em` font sizing in all browsers.
218 | */
219 |
220 | code,
221 | kbd,
222 | pre,
223 | samp {
224 | font-family: monospace, monospace; /* 1 */
225 | font-size: 1em; /* 2 */
226 | }
227 |
228 | /**
229 | * Add the correct margin in IE 8.
230 | */
231 |
232 | figure {
233 | margin: 1em 40px;
234 | }
235 |
236 | /**
237 | * 1. Add the correct box sizing in Firefox.
238 | * 2. Show the overflow in Edge and IE.
239 | */
240 |
241 | hr {
242 | box-sizing: content-box; /* 1 */
243 | height: 0; /* 1 */
244 | overflow: visible; /* 2 */
245 | }
246 |
247 | /* Forms
248 | ========================================================================== */
249 |
250 | /**
251 | * Change font properties to `inherit` in all browsers (opinionated).
252 | */
253 |
254 | button,
255 | input,
256 | select,
257 | textarea {
258 | font: inherit;
259 | }
260 |
261 | /**
262 | * Restore the font weight unset by the previous rule.
263 | */
264 |
265 | optgroup {
266 | font-weight: bold;
267 | }
268 |
269 | /**
270 | * Show the overflow in IE.
271 | * 1. Show the overflow in Edge.
272 | * 2. Show the overflow in Edge, Firefox, and IE.
273 | */
274 |
275 | button,
276 | input, /* 1 */
277 | select { /* 2 */
278 | overflow: visible;
279 | }
280 |
281 | /**
282 | * Remove the margin in Safari.
283 | * 1. Remove the margin in Firefox and Safari.
284 | */
285 |
286 | button,
287 | input,
288 | select,
289 | textarea { /* 1 */
290 | margin: 0;
291 | }
292 |
293 | /**
294 | * Remove the inheritence of text transform in Edge, Firefox, and IE.
295 | * 1. Remove the inheritence of text transform in Firefox.
296 | */
297 |
298 | button,
299 | select { /* 1 */
300 | text-transform: none;
301 | }
302 |
303 | /**
304 | * Change the cursor in all browsers (opinionated).
305 | */
306 |
307 | button,
308 | [type="button"],
309 | [type="reset"],
310 | [type="submit"] {
311 | cursor: pointer;
312 | }
313 |
314 | /**
315 | * Restore the default cursor to disabled elements unset by the previous rule.
316 | */
317 |
318 | [disabled] {
319 | cursor: default;
320 | }
321 |
322 | /**
323 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
324 | * controls in Android 4.
325 | * 2. Correct the inability to style clickable types in iOS.
326 | */
327 |
328 | button,
329 | html [type="button"], /* 1 */
330 | [type="reset"],
331 | [type="submit"] {
332 | -webkit-appearance: button; /* 2 */
333 | }
334 |
335 | /**
336 | * Remove the inner border and padding in Firefox.
337 | */
338 |
339 | button::-moz-focus-inner,
340 | input::-moz-focus-inner {
341 | border: 0;
342 | padding: 0;
343 | }
344 |
345 | /**
346 | * Restore the focus styles unset by the previous rule.
347 | */
348 |
349 | button:-moz-focusring,
350 | input:-moz-focusring {
351 | outline: 1px dotted ButtonText;
352 | }
353 |
354 | /**
355 | * Change the border, margin, and padding in all browsers (opinionated).
356 | */
357 |
358 | fieldset {
359 | border: 1px solid #c0c0c0;
360 | margin: 0 2px;
361 | padding: 0.35em 0.625em 0.75em;
362 | }
363 |
364 | /**
365 | * 1. Correct the text wrapping in Edge and IE.
366 | * 2. Correct the color inheritance from `fieldset` elements in IE.
367 | * 3. Remove the padding so developers are not caught out when they zero out
368 | * `fieldset` elements in all browsers.
369 | */
370 |
371 | legend {
372 | box-sizing: border-box; /* 1 */
373 | color: inherit; /* 2 */
374 | display: table; /* 1 */
375 | max-width: 100%; /* 1 */
376 | padding: 0; /* 3 */
377 | white-space: normal; /* 1 */
378 | }
379 |
380 | /**
381 | * Remove the default vertical scrollbar in IE.
382 | */
383 |
384 | textarea {
385 | overflow: auto;
386 | }
387 |
388 | /**
389 | * 1. Add the correct box sizing in IE 10-.
390 | * 2. Remove the padding in IE 10-.
391 | */
392 |
393 | [type="checkbox"],
394 | [type="radio"] {
395 | box-sizing: border-box; /* 1 */
396 | padding: 0; /* 2 */
397 | }
398 |
399 | /**
400 | * Correct the cursor style of increment and decrement buttons in Chrome.
401 | */
402 |
403 | [type="number"]::-webkit-inner-spin-button,
404 | [type="number"]::-webkit-outer-spin-button {
405 | height: auto;
406 | }
407 |
408 | /**
409 | * Correct the odd appearance of search inputs in Chrome and Safari.
410 | */
411 |
412 | [type="search"] {
413 | -webkit-appearance: textfield;
414 | }
415 |
416 | /**
417 | * Remove the inner padding and cancel buttons in Chrome on OS X and
418 | * Safari on OS X.
419 | */
420 |
421 | [type="search"]::-webkit-search-cancel-button,
422 | [type="search"]::-webkit-search-decoration {
423 | -webkit-appearance: none;
424 | }
425 |
--------------------------------------------------------------------------------
/src/scss/_vars.scss:
--------------------------------------------------------------------------------
1 | /* FONT */
2 | $font-family: Georgia, serif;
3 | $font-size: 16px;
4 |
5 | /* COLORS */
6 | $color-font: #222;
7 | $color-link: darkslategray;
8 | $color-link-visited: darkcyan;
9 | $color-link-hover: cadetblue;
10 |
11 |
12 | /* BREAKPOINTS */
13 | $bp-mobile: 320px;
14 | $bp-tablet: 768px;
15 | $bp-desktop: 1000px;
16 |
--------------------------------------------------------------------------------
/src/scss/blocks/_attrs.scss:
--------------------------------------------------------------------------------
1 | .attrs {
2 | display: flex;
3 | align-items: center;
4 |
5 | &__item {
6 | position: relative;
7 |
8 | &--line {
9 | display: flex;
10 | flex-direction: row;
11 | align-items: center;
12 | margin: 0 1em 0 0;
13 | }
14 | }
15 |
16 | &__input {
17 | min-width: 0;
18 | width: 1em;
19 | text-align: center;
20 | background: transparent;
21 | border: 2px solid transparent;
22 | margin: 0 1px;
23 | padding: 2px 0 1px;
24 | border-radius: 3px;
25 | line-height: 1;
26 | color: #FFF;
27 | transition: all .25s;
28 | font-size: 1.2rem;
29 | font-family: Courier New, Courier, monospace;
30 |
31 | &--boolean {
32 | position: absolute;
33 | opacity: 0;
34 | }
35 |
36 | &:focus {
37 | outline: 0;
38 | color: yellowgreen;
39 | border-color: currentColor;
40 | }
41 |
42 | &--string {
43 | width: 1em;
44 | color: lightseagreen;
45 | }
46 |
47 | &--number {
48 | width: 2em;
49 | color: gold;
50 |
51 | }
52 |
53 | &--largeArc,
54 | &--sweep,
55 | &--xRot,
56 | &--repeat,
57 | &--strokeWidth {
58 | width: 1em;
59 | }
60 |
61 | &--error,
62 | &--error:focus {
63 | color: orangered;
64 | border-color: currentColor;
65 | }
66 | }
67 |
68 | &__label {
69 | position: relative;
70 | display: flex;
71 | align-items: center;
72 | font: 16px/1 Arial, sans-serif;
73 | color: yellowgreen;
74 | transition: all .25s;
75 | order: -1;
76 |
77 | &--hidden {
78 | position: absolute;
79 | top: -1.3em;
80 | left: 50%;
81 | transform: translateX(-50%);
82 | opacity: 0;
83 | }
84 |
85 | &--line {
86 | margin-right: .5em;
87 | }
88 | }
89 |
90 | &__input--boolean:disabled ~ &__label {
91 | color: #333;
92 | }
93 |
94 | &__input--boolean ~ &__label::before {
95 | content: '';
96 | display: block;
97 | width: 1em;
98 | height: 1em;
99 | margin-right: .5em;
100 | border-radius: 3px;
101 | border: 2px solid #222;
102 | }
103 |
104 | &__input--boolean ~ &__label::after {
105 | content: '';
106 | display: block;
107 | position: absolute;
108 | top: 0;
109 | left: 0;
110 | width: 1em;
111 | height: 1em;
112 | margin: 2px;
113 | background-position: center center;
114 | background-repeat: no-repeat;
115 | }
116 |
117 | &__input--boolean:checked ~ &__label::after {
118 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 102 63'%3E%3Cpolyline fill='none' stroke='yellowgreen' stroke-width='15' points='1.938 20.461 37.484 60.129 100.637 2.238'/%3E%3C/svg%3E%0A");
119 | }
120 | &__input--boolean:checked:disabled ~ &__label::after {
121 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 102 63'%3E%3Cpolyline fill='none' stroke='%23222' stroke-width='15' points='1.938 20.461 37.484 60.129 100.637 2.238'/%3E%3C/svg%3E%0A");
122 | }
123 |
124 | &__input--boolean:focus ~ &__label::before {
125 | border-color: yellowgreen;
126 | }
127 |
128 | &__input:focus ~ &__label {
129 | opacity: 1;
130 | }
131 |
132 | &__input--error ~ &__label {
133 | color: orangered;
134 | }
135 |
136 | &__error {
137 | position: absolute;
138 | bottom: -1.3em;
139 | left: 50%;
140 | opacity: 0;
141 | transform: translateX(-50%);
142 | font: 16px/1 Arial, sans-serif;
143 | color: orangered;
144 | }
145 |
146 | &__item--line &__error {
147 | top: -1.3em;
148 | bottom: auto;
149 | }
150 |
151 | &__input--error:focus ~ &__error {
152 | opacity: 1;
153 | }
154 |
155 | &__text {
156 | margin-right: 20px;
157 | font: 16px/1 Arial, sans-serif;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/scss/blocks/_code.scss:
--------------------------------------------------------------------------------
1 | .code {
2 | position: relative;
3 | margin-left: 20px;
4 |
5 | &__textarea {
6 | background: transparent;
7 | border: 3px solid transparent;
8 | border-radius: 5px;
9 | font: inherit;
10 | font-size: 14px;
11 | line-height: 1.3;
12 | font-family: Courier New, Courier, monospace;
13 |
14 | &:focus {
15 | outline: 0;
16 | border-color: yellowgreen;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/scss/blocks/_flags.scss:
--------------------------------------------------------------------------------
1 | .flags {
2 | position: relative;
3 | margin-top: 10px;
4 | min-height: 30px;
5 |
6 | @media (min-width: 700px) {
7 | margin-top: 0;
8 | }
9 |
10 | &__text {
11 | position: absolute;
12 | left: 0;
13 | bottom: -1.3em;
14 | font-size: 14px;
15 | line-height: 1;
16 | color: #333;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/scss/blocks/_footer.scss:
--------------------------------------------------------------------------------
1 | .footer {
2 | @include container-width;
3 |
4 | display: flex;
5 |
6 | margin: 1rem auto;
7 | padding: .5rem;
8 |
9 | background: beige;
10 | border: 1px solid rgba(0,0,0,.1);
11 |
12 | }
13 |
14 | .footer__items {
15 | display: flex;
16 | justify-content: space-between;
17 | align-items: center;
18 |
19 | width: 100%;
20 |
21 | @media ( max-width: $bp-tablet ) {
22 | flex-direction: column;
23 | }
24 | }
25 |
26 | .socials {
27 |
28 | }
29 | .socials__item {
30 | display: inline-block;
31 | margin: 0 .5rem;
32 | }
33 |
--------------------------------------------------------------------------------
/src/scss/blocks/_github.scss:
--------------------------------------------------------------------------------
1 | .github-link {
2 | position: absolute;
3 | right: 1rem;
4 | top: 50%;
5 | transform: translate(0,-50%);
6 | color: teal;
7 | transition: all .25s;
8 |
9 | &:visited {
10 | color: #333;
11 | }
12 |
13 | &:hover {
14 | color: yellowgreen;
15 | }
16 | }
17 |
18 | .github-icon {
19 | display: block;
20 | width: 24px;
21 | height: 24px;
22 | }
--------------------------------------------------------------------------------
/src/scss/blocks/_header.scss:
--------------------------------------------------------------------------------
1 | .header {
2 | @include container-width;
3 |
4 | margin: 1rem auto;
5 | padding: .5rem;
6 |
7 | display: flex;
8 | justify-content: space-between;
9 | align-items: center;
10 |
11 | @media ( max-width: $bp-tablet ) {
12 | flex-direction: column;
13 | }
14 |
15 | background: khaki;
16 | border: 1px solid rgba(0,0,0,.1);
17 | }
18 |
19 | .nav {
20 |
21 | }
22 | .nav__item {
23 | display: inline-block;
24 | margin: 0 .5rem;
25 | }
26 |
--------------------------------------------------------------------------------
/src/scss/blocks/_panel.scss:
--------------------------------------------------------------------------------
1 | .panel {
2 | position: relative;
3 | min-height: 60px;
4 | padding: 2rem 3rem;
5 | box-sizing: border-box;
6 | background: #000;
7 |
8 | display: flex;
9 | flex-direction: row;
10 | flex-wrap: wrap;
11 | justify-content: center;
12 | align-items: center;
13 |
14 | font-size: 16px;
15 | font-family: Arial, sans-serif;
16 | color: #555;
17 |
18 | &--codefont {
19 | font-size: 1.2rem;
20 | font-family: Courier New, Courier, monospace;
21 | }
22 |
23 | &__inner {
24 | display: flex;
25 | flex-direction: row;
26 | align-items: baseline;
27 | white-space: nowrap;
28 | line-height: 2;
29 | }
30 |
31 | &__column {
32 | margin-bottom: 1em;
33 | display: flex;
34 | flex-direction: row;
35 | flex-basis: 50%;
36 |
37 | &--props {
38 | flex-wrap: wrap;
39 |
40 | @media (min-width: 700px) {
41 | flex-wrap: nowrap;
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/scss/blocks/_popup.scss:
--------------------------------------------------------------------------------
1 | .popup {
2 | position: relative;
3 | margin-left: 20px;
4 | align-self: baseline;
5 |
6 | &__toggle {
7 | height: 30px;
8 | padding: 0 10px;
9 | background: transparent;
10 | border: 2px solid #222;
11 | border-radius: 3px;
12 | line-height: 30px;
13 | font: 16px/1 Arial, sans-serif;
14 | color: yellowgreen;
15 |
16 | &:focus {
17 | outline: 0;
18 | border-color: currentColor;
19 | }
20 | }
21 |
22 | &__container {
23 | position: absolute;
24 | z-index: 20;
25 | bottom: 45px;
26 | left: 50%;
27 | transform: translateX(-50%);
28 | height: 0;
29 | min-height: 0;
30 | overflow: hidden;
31 | display: flex;
32 | border-radius: 5px;
33 | transition: all .4s;
34 | }
35 |
36 | &:last-child &__container {
37 | @media (max-width: 1000px) {
38 | left: auto;
39 | right: 0;
40 | transform: none;
41 | }
42 | }
43 |
44 | &--opened &__container {
45 | height: 300px;
46 | box-shadow: 0 0 10px rgba(0,0,0,.5),
47 | 0 0 15px rgba(0,0,0,.5);
48 | }
49 |
50 | &__content {
51 | width: 350px;
52 | display: flex;
53 | padding: 10px;
54 | background: rgba(255,255,255,.9);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/scss/blocks/_svg.scss:
--------------------------------------------------------------------------------
1 | .svg {
2 | position: relative;
3 | z-index: 10;
4 | width: 100%;
5 | flex-shrink: 0;
6 | flex-grow: 1;
7 | margin: auto;
8 | overflow: visible !important;
9 | box-sizing: border-box;
10 | border: 1px solid transparent;
11 | border-color: transparent #DDD #DDD transparent;
12 | background:
13 | linear-gradient(to bottom, #DDD 1px, transparent 0),
14 | linear-gradient(to right, #DDD 1px, transparent 0);
15 | background-size: 50px 50px;
16 | }
17 |
18 | .point-control {
19 | stroke: #000;
20 | stroke-width: 3;
21 | fill: greenyellow;
22 | cursor: pointer;
23 | transition: fill .25s;
24 |
25 | &:hover {
26 | fill: orangered;
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/scss/blocks/_wave-types.scss:
--------------------------------------------------------------------------------
1 | .wave-types {
2 |
3 | &__items {
4 | display: flex;
5 | flex-direction: row;
6 | flex-wrap: wrap;
7 | }
8 |
9 | &__item {
10 | margin: 3px;
11 | flex-basis: 40%;
12 | flex-grow: 1;
13 | }
14 |
15 | &__button {
16 | width: 100%;
17 | height: 60px;
18 | padding: 3px 10px;
19 | display: flex;
20 | background: transparent;
21 | border: 2px solid #999;
22 | border-radius: 3px;
23 | cursor: pointer;
24 |
25 | svg {
26 | width: 100%;
27 | height: 100%;
28 | }
29 |
30 | &:focus {
31 | outline: 0;
32 | color: yellowgreen;
33 | border-color: currentColor;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/scss/styles.scss:
--------------------------------------------------------------------------------
1 | @import '_vars';
2 | @import '_mixins';
3 |
4 | @import '_normalize';
5 | @import '_base';
6 | @import 'blocks/_github';
7 | @import 'blocks/_svg';
8 |
9 | @import 'blocks/_header';
10 | @import 'blocks/_footer';
11 |
12 | @import 'blocks/_panel';
13 | @import 'blocks/_attrs';
14 | @import 'blocks/_code';
15 | @import 'blocks/_popup';
16 | @import 'blocks/_wave-types';
17 | @import 'blocks/_flags';
18 |
19 |
--------------------------------------------------------------------------------