├── static └── .gitkeep ├── src ├── icons │ └── index.js ├── sass │ ├── utilities │ │ ├── _clearfix.scss │ │ ├── _overflow.scss │ │ ├── _visibility.scss │ │ ├── _shadows.scss │ │ ├── _float.scss │ │ ├── _align.scss │ │ ├── _background.scss │ │ ├── _stretched-link.scss │ │ ├── _sizing.scss │ │ ├── _position.scss │ │ ├── _display.scss │ │ ├── _embed.scss │ │ ├── _borders.scss │ │ ├── _text.scss │ │ ├── _spacing.scss │ │ └── _flex.scss │ ├── mixins │ │ ├── _clearfix.scss │ │ ├── _size.scss │ │ ├── _lists.scss │ │ ├── _text-truncate.scss │ │ ├── _visibility.scss │ │ ├── _resize.scss │ │ ├── _alert.scss │ │ ├── _nav-divider.scss │ │ ├── _text-hide.scss │ │ ├── _badge.scss │ │ ├── _transition.scss │ │ ├── _text-emphasis.scss │ │ ├── _float.scss │ │ ├── _list-group.scss │ │ ├── _reset-text.scss │ │ ├── _pagination.scss │ │ ├── _background-variant.scss │ │ ├── _box-shadow.scss │ │ ├── _deprecate.scss │ │ ├── _screen-reader.scss │ │ ├── _hover.scss │ │ ├── _table-row.scss │ │ ├── _image.scss │ │ ├── _border-radius.scss │ │ ├── _caret.scss │ │ ├── _grid.scss │ │ ├── _grid-framework.scss │ │ ├── _gradients.scss │ │ ├── _buttons.scss │ │ ├── _breakpoints.scss │ │ └── _forms.scss │ ├── bootstrap-reboot.scss │ ├── _utilities.scss │ ├── _root.scss │ ├── bootstrap-grid.scss │ ├── bootstrap.scss │ ├── _transitions.scss │ ├── _grid.scss │ ├── _mixins.scss │ ├── _images.scss │ ├── _badge.scss │ ├── _type.scss │ ├── _nav.scss │ ├── _functions.scss │ ├── _buttons.scss │ ├── _button-group.scss │ ├── _custom.scss │ ├── _input-group.scss │ ├── vendor │ │ └── _rfs.scss │ ├── _forms.scss │ ├── _reboot.scss │ └── _custom-forms.scss ├── lib │ ├── hash.js │ ├── svgDownload.js │ ├── simplify-path.js │ ├── math.js │ └── function-generator.js ├── main.js ├── components │ ├── Toggle.vue │ ├── SelectField.vue │ ├── Slider.vue │ ├── TextInput.vue │ └── ColorPicker │ │ ├── Checkboard.vue │ │ ├── colorMixin.js │ │ ├── EditableInput.vue │ │ ├── Alpha.vue │ │ ├── Saturation.vue │ │ ├── Hue.vue │ │ └── ColorPicker.vue ├── help │ └── text.js └── appState.js ├── .browserslistrc ├── .eslintignore ├── docs ├── sample-1.jpg ├── sample-2.jpeg ├── sample-3.jpeg ├── sample-4.jpeg ├── flow-lines.gif ├── index.html └── css │ └── chunk-vendors.a2cbee8a.css ├── postcss.config.js ├── babel.config.js ├── .github └── FUNDING.yml ├── .editorconfig ├── vue.config.js ├── .gitignore ├── .eslintrc.js ├── public └── index.html ├── LICENSE ├── package.json └── README.md /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | -------------------------------------------------------------------------------- /src/sass/utilities/_clearfix.scss: -------------------------------------------------------------------------------- 1 | .clearfix { 2 | @include clearfix(); 3 | } 4 | -------------------------------------------------------------------------------- /docs/sample-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msurguy/flow-lines/master/docs/sample-1.jpg -------------------------------------------------------------------------------- /docs/sample-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msurguy/flow-lines/master/docs/sample-2.jpeg -------------------------------------------------------------------------------- /docs/sample-3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msurguy/flow-lines/master/docs/sample-3.jpeg -------------------------------------------------------------------------------- /docs/sample-4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msurguy/flow-lines/master/docs/sample-4.jpeg -------------------------------------------------------------------------------- /docs/flow-lines.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msurguy/flow-lines/master/docs/flow-lines.gif -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/sass/mixins/_clearfix.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix() { 2 | &::after { 3 | display: block; 4 | clear: both; 5 | content: ""; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: msurguy 4 | open_collective: drawingbots-community 5 | ko_fi: msurguy 6 | -------------------------------------------------------------------------------- /src/sass/utilities/_overflow.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | @each $value in $overflows { 4 | .overflow-#{$value} { overflow: $value !important; } 5 | } 6 | -------------------------------------------------------------------------------- /src/sass/mixins/_size.scss: -------------------------------------------------------------------------------- 1 | // Sizing shortcuts 2 | 3 | @mixin size($width, $height: $width) { 4 | width: $width; 5 | height: $height; 6 | @include deprecate("`size()`", "v4.3.0", "v5"); 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/sass/mixins/_lists.scss: -------------------------------------------------------------------------------- 1 | // Lists 2 | 3 | // Unstyled keeps list items block level, just removes default browser padding and list-style 4 | @mixin list-unstyled { 5 | padding-left: 0; 6 | list-style: none; 7 | } 8 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: process.env.NODE_ENV === 'production' 3 | ? '/flow-lines/' 4 | : '/', 5 | outputDir: 'docs', 6 | devServer: { 7 | port: 8085 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /src/sass/mixins/_text-truncate.scss: -------------------------------------------------------------------------------- 1 | // Text truncate 2 | // Requires inline-block or block for proper styling 3 | 4 | @mixin text-truncate() { 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | white-space: nowrap; 8 | } 9 | -------------------------------------------------------------------------------- /src/sass/mixins/_visibility.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // Visibility 4 | 5 | @mixin invisible($visibility) { 6 | visibility: $visibility !important; 7 | @include deprecate("`invisible()`", "v4.3.0", "v5"); 8 | } 9 | -------------------------------------------------------------------------------- /src/sass/mixins/_resize.scss: -------------------------------------------------------------------------------- 1 | // Resize anything 2 | 3 | @mixin resizable($direction) { 4 | overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` 5 | resize: $direction; // Options: horizontal, vertical, both 6 | } 7 | -------------------------------------------------------------------------------- /src/sass/utilities/_visibility.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // 4 | // Visibility utilities 5 | // 6 | 7 | .visible { 8 | visibility: visible !important; 9 | } 10 | 11 | .invisible { 12 | visibility: hidden !important; 13 | } 14 | -------------------------------------------------------------------------------- /src/sass/utilities/_shadows.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | .shadow-sm { box-shadow: $box-shadow-sm !important; } 4 | .shadow { box-shadow: $box-shadow !important; } 5 | .shadow-lg { box-shadow: $box-shadow-lg !important; } 6 | .shadow-none { box-shadow: none !important; } 7 | -------------------------------------------------------------------------------- /src/sass/mixins/_alert.scss: -------------------------------------------------------------------------------- 1 | @mixin alert-variant($background, $border, $color) { 2 | color: $color; 3 | @include gradient-bg($background); 4 | border-color: $border; 5 | 6 | hr { 7 | border-top-color: darken($border, 5%); 8 | } 9 | 10 | .alert-link { 11 | color: darken($color, 10%); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/sass/mixins/_nav-divider.scss: -------------------------------------------------------------------------------- 1 | // Horizontal dividers 2 | // 3 | // Dividers (basically an hr) within dropdowns and nav lists 4 | 5 | @mixin nav-divider($color: $nav-divider-color, $margin-y: $nav-divider-margin-y) { 6 | height: 0; 7 | margin: $margin-y 0; 8 | overflow: hidden; 9 | border-top: 1px solid $color; 10 | } 11 | -------------------------------------------------------------------------------- /src/sass/mixins/_text-hide.scss: -------------------------------------------------------------------------------- 1 | // CSS image replacement 2 | @mixin text-hide($ignore-warning: false) { 3 | // stylelint-disable-next-line font-family-no-missing-generic-family-keyword 4 | font: 0/0 a; 5 | color: transparent; 6 | text-shadow: none; 7 | background-color: transparent; 8 | border: 0; 9 | 10 | @include deprecate("`text-hide()`", "v4.1.0", "v5", $ignore-warning); 11 | } 12 | -------------------------------------------------------------------------------- /src/sass/mixins/_badge.scss: -------------------------------------------------------------------------------- 1 | @mixin badge-variant($bg) { 2 | color: color-yiq($bg); 3 | background-color: $bg; 4 | 5 | @at-root a#{&} { 6 | @include hover-focus { 7 | color: color-yiq($bg); 8 | background-color: darken($bg, 10%); 9 | } 10 | 11 | &:focus, 12 | &.focus { 13 | outline: 0; 14 | box-shadow: 0 0 0 $badge-focus-width rgba($bg, .5); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/standard' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/hash.js: -------------------------------------------------------------------------------- 1 | export const hash = (str) => { 2 | let hash = 5381 3 | let i = str.length 4 | 5 | while (i) { 6 | hash = (hash * 33) ^ str.charCodeAt(--i) 7 | } 8 | 9 | /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed 10 | * integers. Since we want the results to be always positive, convert the 11 | * signed int to an unsigned by doing an unsigned bitshift. */ 12 | return hash >>> 0 13 | } 14 | -------------------------------------------------------------------------------- /src/sass/utilities/_float.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | @each $breakpoint in map-keys($grid-breakpoints) { 4 | @include media-breakpoint-up($breakpoint) { 5 | $infix: breakpoint-infix($breakpoint, $grid-breakpoints); 6 | 7 | .float#{$infix}-left { float: left !important; } 8 | .float#{$infix}-right { float: right !important; } 9 | .float#{$infix}-none { float: none !important; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/sass/mixins/_transition.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable property-blacklist 2 | @mixin transition($transition...) { 3 | @if $enable-transitions { 4 | @if length($transition) == 0 { 5 | transition: $transition-base; 6 | } @else { 7 | transition: $transition; 8 | } 9 | } 10 | 11 | @if $enable-prefers-reduced-motion-media-query { 12 | @media (prefers-reduced-motion: reduce) { 13 | transition: none; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/sass/mixins/_text-emphasis.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // Typography 4 | 5 | @mixin text-emphasis-variant($parent, $color) { 6 | #{$parent} { 7 | color: $color !important; 8 | } 9 | @if $emphasized-link-hover-darken-percentage != 0 { 10 | a#{$parent} { 11 | @include hover-focus { 12 | color: darken($color, $emphasized-link-hover-darken-percentage) !important; 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/sass/utilities/_align.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | .align-baseline { vertical-align: baseline !important; } // Browser default 4 | .align-top { vertical-align: top !important; } 5 | .align-middle { vertical-align: middle !important; } 6 | .align-bottom { vertical-align: bottom !important; } 7 | .align-text-bottom { vertical-align: text-bottom !important; } 8 | .align-text-top { vertical-align: text-top !important; } 9 | -------------------------------------------------------------------------------- /src/sass/mixins/_float.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | @mixin float-left { 4 | float: left !important; 5 | @include deprecate("The `float-left` mixin", "v4.3.0", "v5"); 6 | } 7 | @mixin float-right { 8 | float: right !important; 9 | @include deprecate("The `float-right` mixin", "v4.3.0", "v5"); 10 | } 11 | @mixin float-none { 12 | float: none !important; 13 | @include deprecate("The `float-none` mixin", "v4.3.0", "v5"); 14 | } 15 | -------------------------------------------------------------------------------- /src/sass/bootstrap-reboot.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.0 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | 9 | @import "functions"; 10 | @import "variables"; 11 | @import "mixins"; 12 | @import "reboot"; 13 | -------------------------------------------------------------------------------- /src/sass/utilities/_background.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | @each $color, $value in $theme-colors { 4 | @include bg-variant(".bg-#{$color}", $value); 5 | } 6 | 7 | @if $enable-gradients { 8 | @each $color, $value in $theme-colors { 9 | @include bg-gradient-variant(".bg-gradient-#{$color}", $value); 10 | } 11 | } 12 | 13 | .bg-white { 14 | background-color: $white !important; 15 | } 16 | 17 | .bg-transparent { 18 | background-color: transparent !important; 19 | } 20 | -------------------------------------------------------------------------------- /src/sass/utilities/_stretched-link.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Stretched link 3 | // 4 | 5 | .stretched-link { 6 | &::after { 7 | position: absolute; 8 | top: 0; 9 | right: 0; 10 | bottom: 0; 11 | left: 0; 12 | z-index: 1; 13 | // Just in case `pointer-events: none` is set on a parent 14 | pointer-events: auto; 15 | content: ""; 16 | // IE10 bugfix, see https://stackoverflow.com/questions/16947967/ie10-hover-pseudo-class-doesnt-work-without-background-color 17 | background-color: rgba(0, 0, 0, 0); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/sass/mixins/_list-group.scss: -------------------------------------------------------------------------------- 1 | // List Groups 2 | 3 | @mixin list-group-item-variant($state, $background, $color) { 4 | .list-group-item-#{$state} { 5 | color: $color; 6 | background-color: $background; 7 | 8 | &.list-group-item-action { 9 | @include hover-focus { 10 | color: $color; 11 | background-color: darken($background, 5%); 12 | } 13 | 14 | &.active { 15 | color: $white; 16 | background-color: $color; 17 | border-color: $color; 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/sass/mixins/_reset-text.scss: -------------------------------------------------------------------------------- 1 | @mixin reset-text { 2 | font-family: $font-family-base; 3 | // We deliberately do NOT reset font-size or word-wrap. 4 | font-style: normal; 5 | font-weight: $font-weight-normal; 6 | line-height: $line-height-base; 7 | text-align: left; // Fallback for where `start` is not supported 8 | text-align: start; 9 | text-decoration: none; 10 | text-shadow: none; 11 | text-transform: none; 12 | letter-spacing: normal; 13 | word-break: normal; 14 | word-spacing: normal; 15 | white-space: normal; 16 | line-break: auto; 17 | } 18 | -------------------------------------------------------------------------------- /src/sass/mixins/_pagination.scss: -------------------------------------------------------------------------------- 1 | // Pagination 2 | 3 | @mixin pagination-size($padding-y, $padding-x, $font-size, $line-height, $border-radius) { 4 | .page-link { 5 | padding: $padding-y $padding-x; 6 | @include font-size($font-size); 7 | line-height: $line-height; 8 | } 9 | 10 | .page-item { 11 | &:first-child { 12 | .page-link { 13 | @include border-left-radius($border-radius); 14 | } 15 | } 16 | &:last-child { 17 | .page-link { 18 | @include border-right-radius($border-radius); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/sass/_utilities.scss: -------------------------------------------------------------------------------- 1 | @import "utilities/align"; 2 | @import "utilities/background"; 3 | @import "utilities/borders"; 4 | @import "utilities/clearfix"; 5 | @import "utilities/display"; 6 | // @import "utilities/embed"; 7 | @import "utilities/flex"; 8 | @import "utilities/float"; 9 | @import "utilities/overflow"; 10 | @import "utilities/position"; 11 | //@import "utilities/screenreaders"; 12 | @import "utilities/shadows"; 13 | @import "utilities/sizing"; 14 | // @import "utilities/stretched-link"; 15 | @import "utilities/spacing"; 16 | @import "utilities/text"; 17 | @import "utilities/visibility"; 18 | -------------------------------------------------------------------------------- /src/sass/mixins/_background-variant.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // Contextual backgrounds 4 | 5 | @mixin bg-variant($parent, $color) { 6 | #{$parent} { 7 | background-color: $color !important; 8 | } 9 | a#{$parent}, 10 | button#{$parent} { 11 | @include hover-focus { 12 | background-color: darken($color, 10%) !important; 13 | } 14 | } 15 | } 16 | 17 | @mixin bg-gradient-variant($parent, $color) { 18 | #{$parent} { 19 | background: $color linear-gradient(180deg, mix($body-bg, $color, 15%), $color) repeat-x !important; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import './sass/bootstrap.scss' 6 | import SvgIcon from 'vue-svgicon' 7 | import VueAnalytics from 'vue-analytics' 8 | 9 | Vue.config.productionTip = false 10 | 11 | // Default tag name is 'svgicon' 12 | Vue.use(SvgIcon, { 13 | tagName: 'svgicon' 14 | }) 15 | 16 | Vue.use(VueAnalytics, { 17 | id: 'UA-153496574-1' 18 | }) 19 | 20 | new Vue({ 21 | render: h => h(App) 22 | }).$mount('#app') 23 | -------------------------------------------------------------------------------- /src/sass/utilities/_sizing.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // Width and height 4 | 5 | @each $prop, $abbrev in (width: w, height: h) { 6 | @each $size, $length in $sizes { 7 | .#{$abbrev}-#{$size} { #{$prop}: $length !important; } 8 | } 9 | } 10 | 11 | .mw-100 { max-width: 100% !important; } 12 | .mh-100 { max-height: 100% !important; } 13 | 14 | // Viewport additional helpers 15 | 16 | .min-vw-100 { min-width: 100vw !important; } 17 | .min-vh-100 { min-height: 100vh !important; } 18 | 19 | .vw-100 { width: 100vw !important; } 20 | .vh-100 { height: 100vh !important; } 21 | -------------------------------------------------------------------------------- /src/sass/mixins/_box-shadow.scss: -------------------------------------------------------------------------------- 1 | @mixin box-shadow($shadow...) { 2 | @if $enable-shadows { 3 | $result: (); 4 | 5 | @if (length($shadow) == 1) { 6 | // We can pass `@include box-shadow(none);` 7 | $result: $shadow; 8 | } @else { 9 | // Filter to avoid invalid properties for example `box-shadow: none, 1px 1px black;` 10 | @for $i from 1 through length($shadow) { 11 | @if nth($shadow, $i) != "none" { 12 | $result: append($result, nth($shadow, $i), "comma"); 13 | } 14 | } 15 | } 16 | @if (length($result) > 0) { 17 | box-shadow: $result; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/sass/utilities/_position.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // Common values 4 | @each $position in $positions { 5 | .position-#{$position} { position: $position !important; } 6 | } 7 | 8 | // Shorthand 9 | 10 | .fixed-top { 11 | position: fixed; 12 | top: 0; 13 | right: 0; 14 | left: 0; 15 | z-index: $zindex-fixed; 16 | } 17 | 18 | .fixed-bottom { 19 | position: fixed; 20 | right: 0; 21 | bottom: 0; 22 | left: 0; 23 | z-index: $zindex-fixed; 24 | } 25 | 26 | .sticky-top { 27 | @supports (position: sticky) { 28 | position: sticky; 29 | top: 0; 30 | z-index: $zindex-sticky; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/sass/utilities/_display.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // 4 | // Utilities for common `display` values 5 | // 6 | 7 | @each $breakpoint in map-keys($grid-breakpoints) { 8 | @include media-breakpoint-up($breakpoint) { 9 | $infix: breakpoint-infix($breakpoint, $grid-breakpoints); 10 | 11 | @each $value in $displays { 12 | .d#{$infix}-#{$value} { display: $value !important; } 13 | } 14 | } 15 | } 16 | 17 | 18 | // 19 | // Utilities for toggling `display` in print 20 | // 21 | 22 | @media print { 23 | @each $value in $displays { 24 | .d-print-#{$value} { display: $value !important; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/sass/_root.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | // Custom variable values only support SassScript inside `#{}`. 3 | @each $color, $value in $colors { 4 | --#{$color}: #{$value}; 5 | } 6 | 7 | @each $color, $value in $theme-colors { 8 | --#{$color}: #{$value}; 9 | } 10 | 11 | @each $bp, $value in $grid-breakpoints { 12 | --breakpoint-#{$bp}: #{$value}; 13 | } 14 | 15 | // Use `inspect` for lists so that quoted items keep the quotes. 16 | // See https://github.com/sass/sass/issues/2383#issuecomment-336349172 17 | --font-family-sans-serif: #{inspect($font-family-sans-serif)}; 18 | --font-family-monospace: #{inspect($font-family-monospace)}; 19 | } 20 | -------------------------------------------------------------------------------- /src/sass/mixins/_deprecate.scss: -------------------------------------------------------------------------------- 1 | // Deprecate mixin 2 | // 3 | // This mixin can be used to deprecate mixins or functions. 4 | // `$enable-deprecation-messages` is a global variable, `$ignore-warning` is a variable that can be passed to 5 | // some deprecated mixins to suppress the warning (for example if the mixin is still be used in the current version of Bootstrap) 6 | @mixin deprecate($name, $deprecate-version, $remove-version, $ignore-warning: false) { 7 | @if ($enable-deprecation-messages != false and $ignore-warning != true) { 8 | @warn "#{$name} has been deprecated as of #{$deprecate-version}. It will be removed entirely in #{$remove-version}."; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/sass/bootstrap-grid.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Grid v4.3.0 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | */ 7 | 8 | html { 9 | box-sizing: border-box; 10 | -ms-overflow-style: scrollbar; 11 | } 12 | 13 | *, 14 | *::before, 15 | *::after { 16 | box-sizing: inherit; 17 | } 18 | 19 | // 20 | //@import "functions"; 21 | //@import "variables"; 22 | // 23 | //@import "mixins/breakpoints"; 24 | //@import "mixins/grid-framework"; 25 | //@import "mixins/grid"; 26 | // 27 | //@import "grid"; 28 | //@import "utilities/display"; 29 | //@import "utilities/flex"; 30 | //@import "utilities/spacing"; 31 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Flow Lines by @msurguy 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/sass/bootstrap.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v4.3.0 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | */ 7 | @import url('https://fonts.googleapis.com/css?family=Raleway:200,300,400,500,700'); 8 | 9 | @import "functions"; 10 | @import "variables"; 11 | @import "mixins"; 12 | @import "root"; 13 | @import "reboot"; 14 | @import "type"; 15 | //@import "images"; 16 | //@import "grid"; 17 | @import "forms"; 18 | @import "buttons"; 19 | @import "button-group"; 20 | @import "transitions"; 21 | //@import "input-group"; 22 | @import "custom-forms"; 23 | @import "badge"; 24 | //@import "nav"; 25 | @import "utilities"; 26 | @import "custom"; 27 | -------------------------------------------------------------------------------- /src/sass/mixins/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Only display content to screen readers 2 | // 3 | // See: https://a11yproject.com/posts/how-to-hide-content/ 4 | // See: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/ 5 | 6 | @mixin sr-only { 7 | position: absolute; 8 | width: 1px; 9 | height: 1px; 10 | padding: 0; 11 | overflow: hidden; 12 | clip: rect(0, 0, 0, 0); 13 | white-space: nowrap; 14 | border: 0; 15 | } 16 | 17 | // Use in conjunction with .sr-only to only display content when it's focused. 18 | // 19 | // Useful for "Skip to main content" links; see https://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 20 | // 21 | // Credit: HTML5 Boilerplate 22 | 23 | @mixin sr-only-focusable { 24 | &:active, 25 | &:focus { 26 | position: static; 27 | width: auto; 28 | height: auto; 29 | overflow: visible; 30 | clip: auto; 31 | white-space: normal; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/sass/mixins/_hover.scss: -------------------------------------------------------------------------------- 1 | // Hover mixin and `$enable-hover-media-query` are deprecated. 2 | // 3 | // Originally added during our alphas and maintained during betas, this mixin was 4 | // designed to prevent `:hover` stickiness on iOS-an issue where hover styles 5 | // would persist after initial touch. 6 | // 7 | // For backward compatibility, we've kept these mixins and updated them to 8 | // always return their regular pseudo-classes instead of a shimmed media query. 9 | // 10 | // Issue: https://github.com/twbs/bootstrap/issues/25195 11 | 12 | @mixin hover { 13 | &:hover { @content; } 14 | } 15 | 16 | @mixin hover-focus { 17 | &:hover, 18 | &:focus { 19 | @content; 20 | } 21 | } 22 | 23 | @mixin plain-hover-focus { 24 | &, 25 | &:hover, 26 | &:focus { 27 | @content; 28 | } 29 | } 30 | 31 | @mixin hover-focus-active { 32 | &:hover, 33 | &:focus, 34 | &:active { 35 | @content; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/Toggle.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 40 | -------------------------------------------------------------------------------- /src/sass/mixins/_table-row.scss: -------------------------------------------------------------------------------- 1 | // Tables 2 | 3 | @mixin table-row-variant($state, $background, $border: null) { 4 | // Exact selectors below required to override `.table-striped` and prevent 5 | // inheritance to nested tables. 6 | .table-#{$state} { 7 | &, 8 | > th, 9 | > td { 10 | background-color: $background; 11 | } 12 | 13 | @if $border != null { 14 | th, 15 | td, 16 | thead th, 17 | tbody + tbody { 18 | border-color: $border; 19 | } 20 | } 21 | } 22 | 23 | // Hover states for `.table-hover` 24 | // Note: this is not available for cells or rows within `thead` or `tfoot`. 25 | .table-hover { 26 | $hover-background: darken($background, 5%); 27 | 28 | .table-#{$state} { 29 | @include hover { 30 | background-color: $hover-background; 31 | 32 | > td, 33 | > th { 34 | background-color: $hover-background; 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/sass/_transitions.scss: -------------------------------------------------------------------------------- 1 | .fade { 2 | @include transition($transition-fade); 3 | 4 | &:not(.show) { 5 | opacity: 0; 6 | } 7 | } 8 | 9 | .collapse { 10 | &:not(.show) { 11 | display: none; 12 | } 13 | } 14 | 15 | .collapsing { 16 | position: relative; 17 | height: 0; 18 | overflow: hidden; 19 | @include transition($transition-collapse); 20 | } 21 | 22 | .slide-enter-active, 23 | .slide-leave-active { 24 | transition: all 250ms ease-in-out; 25 | } 26 | .slide-enter-to, 27 | .slide-leave { 28 | max-height: 400px; 29 | opacity: 1; 30 | overflow: hidden; 31 | } 32 | .slide-enter, 33 | .slide-leave-to { 34 | max-height: 0; 35 | opacity: 0; 36 | overflow: hidden; 37 | } 38 | 39 | .list-button-enter-active, 40 | .list-button-leave-active { 41 | transition: max-width 0.3s ease-in-out;; 42 | } 43 | 44 | .list-button-enter-to, 45 | .list-button-leave { 46 | max-width: 200px; 47 | } 48 | .list-button-enter, 49 | .list-button-leave-to { 50 | max-width: 0; 51 | } 52 | -------------------------------------------------------------------------------- /src/sass/utilities/_embed.scss: -------------------------------------------------------------------------------- 1 | // Credit: Nicolas Gallagher and SUIT CSS. 2 | 3 | .embed-responsive { 4 | position: relative; 5 | display: block; 6 | width: 100%; 7 | padding: 0; 8 | overflow: hidden; 9 | 10 | &::before { 11 | display: block; 12 | content: ""; 13 | } 14 | 15 | .embed-responsive-item, 16 | iframe, 17 | embed, 18 | object, 19 | video { 20 | position: absolute; 21 | top: 0; 22 | bottom: 0; 23 | left: 0; 24 | width: 100%; 25 | height: 100%; 26 | border: 0; 27 | } 28 | } 29 | 30 | @each $embed-responsive-aspect-ratio in $embed-responsive-aspect-ratios { 31 | $embed-responsive-aspect-ratio-x: nth($embed-responsive-aspect-ratio, 1); 32 | $embed-responsive-aspect-ratio-y: nth($embed-responsive-aspect-ratio, 2); 33 | 34 | .embed-responsive-#{$embed-responsive-aspect-ratio-x}by#{$embed-responsive-aspect-ratio-y} { 35 | &::before { 36 | padding-top: percentage($embed-responsive-aspect-ratio-y / $embed-responsive-aspect-ratio-x); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | Flow Lines by @msurguy
-------------------------------------------------------------------------------- /src/lib/svgDownload.js: -------------------------------------------------------------------------------- 1 | export const generateDownload = (element) => { 2 | const svgDoctype = '' 3 | 4 | // serialize our SVG XML to a string. 5 | let svgString = (new XMLSerializer()).serializeToString(element) 6 | 7 | // reduce the SVG path by cutting off floating point values after the first digit beyond floating point (~50% less MBs) 8 | // eslint-disable-next-line no-useless-escape 9 | svgString = svgString.replace(/([\-+]?\d{1,}\.\d{3,}([eE][\-+]?\d+)?)/g, function (x) { 10 | return (+x).toFixed(1) 11 | }) 12 | 13 | const blob = new Blob([svgDoctype + svgString], { type: 'image/svg+xml;charset=utf-8' }) 14 | 15 | /* This portion of script saves the file to local filesystem as a download */ 16 | const svgUrl = URL.createObjectURL(blob) 17 | 18 | const downloadLink = document.createElement('a') 19 | downloadLink.href = svgUrl 20 | downloadLink.download = 'streamlines' + Date.now() + '.svg' 21 | document.body.appendChild(downloadLink) 22 | downloadLink.click() 23 | document.body.removeChild(downloadLink) 24 | URL.revokeObjectURL(svgUrl) 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Maksim Surguy 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 | -------------------------------------------------------------------------------- /src/help/text.js: -------------------------------------------------------------------------------- 1 | export const formulaHelpText = `
Formula Field
2 |

You can use the following:

3 |

4 | Operators:+, -, *, /, %, mod
5 | Arithmetic:abs, exp, log, sqrt, ceil, floor, random, round
6 | Trigonometry:tan, sin, cos, acos, asin, atan, atan2
7 | Statistics:max, min
8 | Constants:pi, e

9 |

Examples:

10 | ` 24 | -------------------------------------------------------------------------------- /src/appState.js: -------------------------------------------------------------------------------- 1 | import queryState from 'query-state' 2 | 3 | export const qs = queryState({}, { useSearch: true }) 4 | 5 | const currentStateFromQuery = qs.get() 6 | 7 | export const defaults = { 8 | xFunction: 'cos((cos(y) - x * y))', 9 | yFunction: 'x', 10 | dTest: 0.005, 11 | separationDistance: 0.2, 12 | simplification: 0.2, 13 | color: '#000000', 14 | bg: null, 15 | timeStep: 0.05, 16 | strokeWidth: 1 17 | } 18 | 19 | export const appState = { 20 | xFunction: currentStateFromQuery.xf || defaults.xFunction, 21 | yFunction: currentStateFromQuery.yf || defaults.yFunction, 22 | seed: currentStateFromQuery.seed || 1, 23 | color: currentStateFromQuery.color || defaults.color, 24 | dTest: currentStateFromQuery.dt || defaults.dTest, 25 | separationDistance: currentStateFromQuery.sd || defaults.separationDistance, // Separation distance between new streamlines. 26 | simplification: currentStateFromQuery.sm || defaults.simplification, // line simplification amount (0.1-2) 27 | timeStep: currentStateFromQuery.ts || defaults.timeStep, 28 | bg: currentStateFromQuery.bg || defaults.bg, 29 | strokeWidth: currentStateFromQuery.sw || defaults.strokeWidth 30 | } 31 | -------------------------------------------------------------------------------- /src/sass/_grid.scss: -------------------------------------------------------------------------------- 1 | // Container widths 2 | // 3 | // Set the container width, and override it for fixed navbars in media queries. 4 | 5 | @if $enable-grid-classes { 6 | .container { 7 | @include make-container(); 8 | @include make-container-max-widths(); 9 | } 10 | } 11 | 12 | // Fluid container 13 | // 14 | // Utilizes the mixin meant for fixed width containers, but with 100% width for 15 | // fluid, full width layouts. 16 | 17 | @if $enable-grid-classes { 18 | .container-fluid { 19 | @include make-container(); 20 | } 21 | } 22 | 23 | // Row 24 | // 25 | // Rows contain and clear the floats of your columns. 26 | 27 | @if $enable-grid-classes { 28 | .row { 29 | @include make-row(); 30 | } 31 | 32 | // Remove the negative margin from default .row, then the horizontal padding 33 | // from all immediate children columns (to prevent runaway style inheritance). 34 | .no-gutters { 35 | margin-right: 0; 36 | margin-left: 0; 37 | 38 | > .col, 39 | > [class*="col-"] { 40 | padding-right: 0; 41 | padding-left: 0; 42 | } 43 | } 44 | } 45 | 46 | // Columns 47 | // 48 | // Common styles for small and large grid columns 49 | 50 | @if $enable-grid-classes { 51 | @include make-grid-columns(); 52 | } 53 | -------------------------------------------------------------------------------- /docs/css/chunk-vendors.a2cbee8a.css: -------------------------------------------------------------------------------- 1 | .popper{width:auto;background-color:#fafafa;color:#212121;text-align:center;padding:2px;display:inline-block;border-radius:3px;position:absolute;font-size:14px;font-weight:400;border:1px solid #ebebeb;z-index:200000;-webkit-box-shadow:#3a3a3a 0 0 6px 0;box-shadow:0 0 6px 0 #3a3a3a}.popper .popper__arrow{width:0;height:0;border-style:solid;position:absolute;margin:5px}.popper[x-placement^=top]{margin-bottom:5px}.popper[x-placement^=top] .popper__arrow{border-width:5px 5px 0 5px;border-color:#fafafa transparent transparent transparent;bottom:-5px;left:calc(50% - 5px);margin-top:0;margin-bottom:0}.popper[x-placement^=bottom]{margin-top:5px}.popper[x-placement^=bottom] .popper__arrow{border-width:0 5px 5px 5px;border-color:transparent transparent #fafafa transparent;top:-5px;left:calc(50% - 5px);margin-top:0;margin-bottom:0}.popper[x-placement^=right]{margin-left:5px}.popper[x-placement^=right] .popper__arrow{border-width:5px 5px 5px 0;border-color:transparent #fafafa transparent transparent;left:-5px;top:calc(50% - 5px);margin-left:0;margin-right:0}.popper[x-placement^=left]{margin-right:5px}.popper[x-placement^=left] .popper__arrow{border-width:5px 0 5px 5px;border-color:transparent transparent transparent #fafafa;right:-5px;top:calc(50% - 5px);margin-left:0;margin-right:0} -------------------------------------------------------------------------------- /src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Toggles 2 | // 3 | // Used in conjunction with global variables to enable certain theme features. 4 | 5 | // Vendor 6 | @import "vendor/rfs"; 7 | 8 | // Deprecate 9 | @import "mixins/deprecate"; 10 | 11 | // Utilities 12 | @import "mixins/breakpoints"; 13 | @import "mixins/hover"; 14 | //@import "mixins/image"; 15 | @import "mixins/badge"; 16 | //@import "mixins/resize"; 17 | //@import "mixins/screen-reader"; 18 | @import "mixins/size"; 19 | @import "mixins/reset-text"; 20 | @import "mixins/text-emphasis"; 21 | //@import "mixins/text-hide"; 22 | //@import "mixins/text-truncate"; 23 | @import "mixins/visibility"; 24 | 25 | // // Components 26 | @import "mixins/alert"; 27 | @import "mixins/buttons"; 28 | @import "mixins/caret"; 29 | @import "mixins/pagination"; 30 | @import "mixins/lists"; 31 | @import "mixins/list-group"; 32 | @import "mixins/nav-divider"; 33 | @import "mixins/forms"; 34 | @import "mixins/table-row"; 35 | 36 | // // Skins 37 | @import "mixins/background-variant"; 38 | @import "mixins/border-radius"; 39 | @import "mixins/box-shadow"; 40 | @import "mixins/gradients"; 41 | @import "mixins/transition"; 42 | 43 | // // Layout 44 | @import "mixins/clearfix"; 45 | //@import "mixins/grid-framework"; 46 | //@import "mixins/grid"; 47 | @import "mixins/float"; 48 | -------------------------------------------------------------------------------- /src/sass/mixins/_image.scss: -------------------------------------------------------------------------------- 1 | // Image Mixins 2 | // - Responsive image 3 | // - Retina image 4 | 5 | 6 | // Responsive image 7 | // 8 | // Keep images from scaling beyond the width of their parents. 9 | 10 | @mixin img-fluid { 11 | // Part 1: Set a maximum relative to the parent 12 | max-width: 100%; 13 | // Part 2: Override the height to auto, otherwise images will be stretched 14 | // when setting a width and height attribute on the img element. 15 | height: auto; 16 | } 17 | 18 | 19 | // Retina image 20 | // 21 | // Short retina mixin for setting background-image and -size. 22 | 23 | @mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) { 24 | background-image: url($file-1x); 25 | 26 | // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio, 27 | // but doesn't convert dppx=>dpi. 28 | // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard. 29 | // Compatibility info: https://caniuse.com/#feat=css-media-resolution 30 | @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx 31 | only screen and (min-resolution: 2dppx) { // Standardized 32 | background-image: url($file-2x); 33 | background-size: $width-1x $height-1x; 34 | } 35 | @include deprecate("`img-retina()`", "v4.3.0", "v5"); 36 | } 37 | -------------------------------------------------------------------------------- /src/sass/_images.scss: -------------------------------------------------------------------------------- 1 | // Responsive images (ensure images don't scale beyond their parents) 2 | // 3 | // This is purposefully opt-in via an explicit class rather than being the default for all ``s. 4 | // We previously tried the "images are responsive by default" approach in Bootstrap v2, 5 | // and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps) 6 | // which weren't expecting the images within themselves to be involuntarily resized. 7 | // See also https://github.com/twbs/bootstrap/issues/18178 8 | .img-fluid { 9 | @include img-fluid; 10 | } 11 | 12 | 13 | // Image thumbnails 14 | .img-thumbnail { 15 | padding: $thumbnail-padding; 16 | background-color: $thumbnail-bg; 17 | border: $thumbnail-border-width solid $thumbnail-border-color; 18 | @include border-radius($thumbnail-border-radius); 19 | @include box-shadow($thumbnail-box-shadow); 20 | 21 | // Keep them at most 100% wide 22 | @include img-fluid; 23 | } 24 | 25 | // 26 | // Figures 27 | // 28 | 29 | .figure { 30 | // Ensures the caption's text aligns with the image. 31 | display: inline-block; 32 | } 33 | 34 | .figure-img { 35 | margin-bottom: $spacer / 2; 36 | line-height: 1; 37 | } 38 | 39 | .figure-caption { 40 | @include font-size($figure-caption-font-size); 41 | color: $figure-caption-color; 42 | } 43 | -------------------------------------------------------------------------------- /src/sass/_badge.scss: -------------------------------------------------------------------------------- 1 | // Base class 2 | // 3 | // Requires one of the contextual, color modifier classes for `color` and 4 | // `background-color`. 5 | 6 | .badge { 7 | display: inline-block; 8 | padding: $badge-padding-y $badge-padding-x; 9 | @include font-size($badge-font-size); 10 | font-weight: $badge-font-weight; 11 | line-height: 1; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | @include border-radius($badge-border-radius); 16 | @include transition($badge-transition); 17 | 18 | @at-root a#{&} { 19 | @include hover-focus { 20 | text-decoration: none; 21 | } 22 | } 23 | 24 | // Empty badges collapse automatically 25 | &:empty { 26 | display: none; 27 | } 28 | } 29 | 30 | // Quick fix for badges in buttons 31 | .btn .badge { 32 | position: relative; 33 | top: -1px; 34 | } 35 | 36 | // Pill badges 37 | // 38 | // Make them extra rounded with a modifier to replace v3's badges. 39 | 40 | .badge-pill { 41 | padding-right: $badge-pill-padding-x; 42 | padding-left: $badge-pill-padding-x; 43 | border-radius: $badge-pill-border-radius; 44 | } 45 | 46 | // Colors 47 | // 48 | // Contextual variations (linked badges get darker on :hover). 49 | 50 | @each $color, $value in $theme-colors { 51 | .badge-#{$color} { 52 | @include badge-variant($value); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flow-lines", 3 | "version": "1.0.0", 4 | "description": "Flow Lines SVG Generator", 5 | "author": "Maksim Surguy ", 6 | "private": false, 7 | "scripts": { 8 | "serve": "vue-cli-service serve", 9 | "svg": "vsvg -s static -t src/icons", 10 | "build": "vue-cli-service build --no-clean", 11 | "lint": "vue-cli-service lint" 12 | }, 13 | "dependencies": { 14 | "@anvaka/streamlines": "^1.1.0", 15 | "clamp": "^1.0.1", 16 | "core-js": "^3.3.2", 17 | "lodash.throttle": "^4.1.1", 18 | "mathjs": "^5.10.3", 19 | "query-state": "^4.2.0", 20 | "randoma": "^1.3.0", 21 | "svg-file-downloader": "0.0.2", 22 | "svg.js": "^2.7.1", 23 | "tinycolor2": "^1.4.1", 24 | "vue": "^2.6.10", 25 | "vue-analytics": "^5.18.0", 26 | "vue-popperjs": "^2.2.0" 27 | }, 28 | "devDependencies": { 29 | "@vue/cli-plugin-babel": "^4.0.0", 30 | "@vue/cli-plugin-eslint": "^4.0.0", 31 | "@vue/cli-service": "^4.0.0", 32 | "@vue/eslint-config-standard": "^4.0.0", 33 | "babel-eslint": "^10.0.3", 34 | "eslint": "^5.16.0", 35 | "eslint-plugin-vue": "^5.0.0", 36 | "node-sass": "^4.12.0", 37 | "sass-loader": "^8.0.0", 38 | "vue-svgicon": "^3.2.6", 39 | "vue-template-compiler": "^2.6.10" 40 | }, 41 | "engines": { 42 | "node": ">= 8.11.0", 43 | "npm": ">= 3.0.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flow Lines 2 | 3 | > A Vue.js project for generating SVGs based on mathematical formulas. 4 | 5 | # Interface 6 | 7 | ![Video](docs/flow-lines.gif) 8 | 9 | # Features 10 | 11 | - Input formula for x / y to get flow field / streamlines / flow lines visualization 12 | - Adjust different parameters such as allowed integration time, separation distance, line parameters 13 | - Save output as SVG graphics 14 | - Mark favorite outputs and come back to them during the same "session" 15 | - Shareable URLs 16 | 17 | # Samples 18 | 19 | Here are some samples of artworks created with this tool: 20 | 21 | ![Sample 1](docs/sample-1.jpg) 22 | ![Sample 2](docs/sample-2.jpeg) 23 | ![Sample 3](docs/sample-3.jpeg) 24 | ![Sample 4](docs/sample-4.jpeg) 25 | 26 | # Support Creator 27 | 28 | If you find this tool useful, I'd like to encourage you to support my efforts by buying me coffee or giving any other amount on these platforms: 29 | 30 | - Patreon: https://www.patreon.com/msurguy 31 | - Open Collective: https://opencollective.com/drawingbots-community 32 | - Ko-Fi: https://ko-fi.com/msurguy 33 | 34 | ## Build Setup 35 | 36 | ``` bash 37 | # install dependencies 38 | npm install 39 | 40 | # serve with hot reload at localhost:8080 41 | npm run dev 42 | 43 | # build for production with minification 44 | npm run build 45 | ``` 46 | 47 | ## Project is using: 48 | 49 | - Vue.js 50 | - Streamlines calculator (https://github.com/anvaka/streamlines) 51 | - SVG.js 52 | - Math.js 53 | - Query state (https://www.npmjs.com/package/query-state) 54 | 55 | ## License 56 | 57 | MIT 58 | -------------------------------------------------------------------------------- /src/sass/mixins/_border-radius.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable property-blacklist 2 | // Single side border-radius 3 | 4 | @mixin border-radius($radius: $border-radius, $fallback-border-radius: false) { 5 | @if $enable-rounded { 6 | border-radius: $radius; 7 | } 8 | @else if $fallback-border-radius != false { 9 | border-radius: $fallback-border-radius; 10 | } 11 | } 12 | 13 | @mixin border-top-radius($radius) { 14 | @if $enable-rounded { 15 | border-top-left-radius: $radius; 16 | border-top-right-radius: $radius; 17 | } 18 | } 19 | 20 | @mixin border-right-radius($radius) { 21 | @if $enable-rounded { 22 | border-top-right-radius: $radius; 23 | border-bottom-right-radius: $radius; 24 | } 25 | } 26 | 27 | @mixin border-bottom-radius($radius) { 28 | @if $enable-rounded { 29 | border-bottom-right-radius: $radius; 30 | border-bottom-left-radius: $radius; 31 | } 32 | } 33 | 34 | @mixin border-left-radius($radius) { 35 | @if $enable-rounded { 36 | border-top-left-radius: $radius; 37 | border-bottom-left-radius: $radius; 38 | } 39 | } 40 | 41 | @mixin border-top-left-radius($radius) { 42 | @if $enable-rounded { 43 | border-top-left-radius: $radius; 44 | } 45 | } 46 | 47 | @mixin border-top-right-radius($radius) { 48 | @if $enable-rounded { 49 | border-top-right-radius: $radius; 50 | } 51 | } 52 | 53 | @mixin border-bottom-right-radius($radius) { 54 | @if $enable-rounded { 55 | border-bottom-right-radius: $radius; 56 | } 57 | } 58 | 59 | @mixin border-bottom-left-radius($radius) { 60 | @if $enable-rounded { 61 | border-bottom-left-radius: $radius; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/sass/mixins/_caret.scss: -------------------------------------------------------------------------------- 1 | @mixin caret-down { 2 | border-top: $caret-width solid; 3 | border-right: $caret-width solid transparent; 4 | border-bottom: 0; 5 | border-left: $caret-width solid transparent; 6 | } 7 | 8 | @mixin caret-up { 9 | border-top: 0; 10 | border-right: $caret-width solid transparent; 11 | border-bottom: $caret-width solid; 12 | border-left: $caret-width solid transparent; 13 | } 14 | 15 | @mixin caret-right { 16 | border-top: $caret-width solid transparent; 17 | border-right: 0; 18 | border-bottom: $caret-width solid transparent; 19 | border-left: $caret-width solid; 20 | } 21 | 22 | @mixin caret-left { 23 | border-top: $caret-width solid transparent; 24 | border-right: $caret-width solid; 25 | border-bottom: $caret-width solid transparent; 26 | } 27 | 28 | @mixin caret($direction: down) { 29 | @if $enable-caret { 30 | &::after { 31 | display: inline-block; 32 | margin-left: $caret-spacing; 33 | vertical-align: $caret-vertical-align; 34 | content: ""; 35 | @if $direction == down { 36 | @include caret-down; 37 | } @else if $direction == up { 38 | @include caret-up; 39 | } @else if $direction == right { 40 | @include caret-right; 41 | } 42 | } 43 | 44 | @if $direction == left { 45 | &::after { 46 | display: none; 47 | } 48 | 49 | &::before { 50 | display: inline-block; 51 | margin-right: $caret-spacing; 52 | vertical-align: $caret-vertical-align; 53 | content: ""; 54 | @include caret-left; 55 | } 56 | } 57 | 58 | &:empty::after { 59 | margin-left: 0; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/lib/simplify-path.js: -------------------------------------------------------------------------------- 1 | // square distance from a point to a segment 2 | const getSqSegDist = (p, p1, p2) => { 3 | let x = p1[0] 4 | let y = p1[1] 5 | let dx = p2[0] - x 6 | let dy = p2[1] - y 7 | 8 | if (dx !== 0 || dy !== 0) { 9 | const t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy) 10 | 11 | if (t > 1) { 12 | x = p2[0] 13 | y = p2[1] 14 | } else if (t > 0) { 15 | x += dx * t 16 | y += dy * t 17 | } 18 | } 19 | 20 | dx = p[0] - x 21 | dy = p[1] - y 22 | 23 | return dx * dx + dy * dy 24 | } 25 | 26 | const simplifyDPStep = (points, first, last, sqTolerance, simplified) => { 27 | let maxSqDist = sqTolerance 28 | let index 29 | 30 | for (let i = first + 1; i < last; i++) { 31 | const sqDist = getSqSegDist(points[i], points[first], points[last]) 32 | 33 | if (sqDist > maxSqDist) { 34 | index = i 35 | maxSqDist = sqDist 36 | } 37 | } 38 | 39 | if (maxSqDist > sqTolerance) { 40 | if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified) 41 | simplified.push(points[index]) 42 | if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified) 43 | } 44 | } 45 | 46 | // simplification using Ramer-Douglas-Peucker algorithm 47 | export const simplify = (points, tolerance) => { 48 | if (points.length <= 1) { 49 | return points 50 | } 51 | tolerance = typeof tolerance === 'number' ? tolerance : 1 52 | const sqTolerance = tolerance * tolerance 53 | 54 | const last = points.length - 1 55 | 56 | const simplified = [points[0]] 57 | simplifyDPStep(points, 0, last, sqTolerance, simplified) 58 | simplified.push(points[last]) 59 | 60 | return simplified 61 | } 62 | -------------------------------------------------------------------------------- /src/sass/mixins/_grid.scss: -------------------------------------------------------------------------------- 1 | /// Grid system 2 | // 3 | // Generate semantic grid columns with these mixins. 4 | 5 | @mixin make-container($gutter: $grid-gutter-width) { 6 | width: 100%; 7 | padding-right: $gutter / 2; 8 | padding-left: $gutter / 2; 9 | margin-right: auto; 10 | margin-left: auto; 11 | } 12 | 13 | 14 | // For each breakpoint, define the maximum width of the container in a media query 15 | @mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) { 16 | @each $breakpoint, $container-max-width in $max-widths { 17 | @include media-breakpoint-up($breakpoint, $breakpoints) { 18 | max-width: $container-max-width; 19 | } 20 | } 21 | } 22 | 23 | @mixin make-row($gutter: $grid-gutter-width) { 24 | display: flex; 25 | flex-wrap: wrap; 26 | margin-right: -$gutter / 2; 27 | margin-left: -$gutter / 2; 28 | } 29 | 30 | @mixin make-col-ready($gutter: $grid-gutter-width) { 31 | position: relative; 32 | // Prevent columns from becoming too narrow when at smaller grid tiers by 33 | // always setting `width: 100%;`. This works because we use `flex` values 34 | // later on to override this initial width. 35 | width: 100%; 36 | padding-right: $gutter / 2; 37 | padding-left: $gutter / 2; 38 | } 39 | 40 | @mixin make-col($size, $columns: $grid-columns) { 41 | flex: 0 0 percentage($size / $columns); 42 | // Add a `max-width` to ensure content within each column does not blow out 43 | // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari 44 | // do not appear to require this. 45 | max-width: percentage($size / $columns); 46 | } 47 | 48 | @mixin make-col-offset($size, $columns: $grid-columns) { 49 | $num: $size / $columns; 50 | margin-left: if($num == 0, 0, percentage($num)); 51 | } 52 | -------------------------------------------------------------------------------- /src/components/SelectField.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 76 | -------------------------------------------------------------------------------- /src/components/Slider.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 74 | -------------------------------------------------------------------------------- /src/sass/utilities/_borders.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable property-blacklist, declaration-no-important 2 | 3 | // 4 | // Border 5 | // 6 | 7 | .border { border: $border-width solid $border-color !important; } 8 | .border-top { border-top: $border-width solid $border-color !important; } 9 | .border-right { border-right: $border-width solid $border-color !important; } 10 | .border-bottom { border-bottom: $border-width solid $border-color !important; } 11 | .border-left { border-left: $border-width solid $border-color !important; } 12 | 13 | .border-0 { border: 0 !important; } 14 | .border-top-0 { border-top: 0 !important; } 15 | .border-right-0 { border-right: 0 !important; } 16 | .border-bottom-0 { border-bottom: 0 !important; } 17 | .border-left-0 { border-left: 0 !important; } 18 | 19 | @each $color, $value in $theme-colors { 20 | .border-#{$color} { 21 | border-color: $value !important; 22 | } 23 | } 24 | 25 | .border-white { 26 | border-color: $white !important; 27 | } 28 | 29 | // 30 | // Border-radius 31 | // 32 | 33 | .rounded-sm { 34 | border-radius: $border-radius-sm !important; 35 | } 36 | 37 | .rounded { 38 | border-radius: $border-radius !important; 39 | } 40 | 41 | .rounded-top { 42 | border-top-left-radius: $border-radius !important; 43 | border-top-right-radius: $border-radius !important; 44 | } 45 | 46 | .rounded-right { 47 | border-top-right-radius: $border-radius !important; 48 | border-bottom-right-radius: $border-radius !important; 49 | } 50 | 51 | .rounded-bottom { 52 | border-bottom-right-radius: $border-radius !important; 53 | border-bottom-left-radius: $border-radius !important; 54 | } 55 | 56 | .rounded-left { 57 | border-top-left-radius: $border-radius !important; 58 | border-bottom-left-radius: $border-radius !important; 59 | } 60 | 61 | .rounded-lg { 62 | border-radius: $border-radius-lg !important; 63 | } 64 | 65 | .rounded-circle { 66 | border-radius: 50% !important; 67 | } 68 | 69 | .rounded-pill { 70 | border-radius: $rounded-pill !important; 71 | } 72 | 73 | .rounded-0 { 74 | border-radius: 0 !important; 75 | } 76 | -------------------------------------------------------------------------------- /src/sass/mixins/_grid-framework.scss: -------------------------------------------------------------------------------- 1 | // Framework grid generation 2 | // 3 | // Used only by Bootstrap to generate the correct number of grid classes given 4 | // any value of `$grid-columns`. 5 | 6 | @mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) { 7 | // Common properties for all breakpoints 8 | %grid-column { 9 | position: relative; 10 | width: 100%; 11 | padding-right: $gutter / 2; 12 | padding-left: $gutter / 2; 13 | } 14 | 15 | @each $breakpoint in map-keys($breakpoints) { 16 | $infix: breakpoint-infix($breakpoint, $breakpoints); 17 | 18 | // Allow columns to stretch full width below their breakpoints 19 | @for $i from 1 through $columns { 20 | .col#{$infix}-#{$i} { 21 | @extend %grid-column; 22 | } 23 | } 24 | .col#{$infix}, 25 | .col#{$infix}-auto { 26 | @extend %grid-column; 27 | } 28 | 29 | @include media-breakpoint-up($breakpoint, $breakpoints) { 30 | // Provide basic `.col-{bp}` classes for equal-width flexbox columns 31 | .col#{$infix} { 32 | flex-basis: 0; 33 | flex-grow: 1; 34 | max-width: 100%; 35 | } 36 | .col#{$infix}-auto { 37 | flex: 0 0 auto; 38 | width: auto; 39 | max-width: 100%; // Reset earlier grid tiers 40 | } 41 | 42 | @for $i from 1 through $columns { 43 | .col#{$infix}-#{$i} { 44 | @include make-col($i, $columns); 45 | } 46 | } 47 | 48 | .order#{$infix}-first { order: -1; } 49 | 50 | .order#{$infix}-last { order: $columns + 1; } 51 | 52 | @for $i from 0 through $columns { 53 | .order#{$infix}-#{$i} { order: $i; } 54 | } 55 | 56 | // `$columns - 1` because offsetting by the width of an entire row isn't possible 57 | @for $i from 0 through ($columns - 1) { 58 | @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0 59 | .offset#{$infix}-#{$i} { 60 | @include make-col-offset($i, $columns); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/components/TextInput.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 72 | 73 | 80 | -------------------------------------------------------------------------------- /src/sass/mixins/_gradients.scss: -------------------------------------------------------------------------------- 1 | // Gradients 2 | 3 | @mixin gradient-bg($color) { 4 | @if $enable-gradients { 5 | background: $color linear-gradient(180deg, mix($body-bg, $color, 15%), $color) repeat-x; 6 | } @else { 7 | background-color: $color; 8 | } 9 | } 10 | 11 | // Horizontal gradient, from left to right 12 | // 13 | // Creates two color stops, start and end, by specifying a color and position for each color stop. 14 | @mixin gradient-x($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) { 15 | background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent); 16 | background-repeat: repeat-x; 17 | } 18 | 19 | // Vertical gradient, from top to bottom 20 | // 21 | // Creates two color stops, start and end, by specifying a color and position for each color stop. 22 | @mixin gradient-y($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) { 23 | background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent); 24 | background-repeat: repeat-x; 25 | } 26 | 27 | @mixin gradient-directional($start-color: $gray-700, $end-color: $gray-800, $deg: 45deg) { 28 | background-image: linear-gradient($deg, $start-color, $end-color); 29 | background-repeat: repeat-x; 30 | } 31 | @mixin gradient-x-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) { 32 | background-image: linear-gradient(to right, $start-color, $mid-color $color-stop, $end-color); 33 | background-repeat: no-repeat; 34 | } 35 | @mixin gradient-y-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) { 36 | background-image: linear-gradient($start-color, $mid-color $color-stop, $end-color); 37 | background-repeat: no-repeat; 38 | } 39 | @mixin gradient-radial($inner-color: $gray-700, $outer-color: $gray-800) { 40 | background-image: radial-gradient(circle, $inner-color, $outer-color); 41 | background-repeat: no-repeat; 42 | } 43 | @mixin gradient-striped($color: rgba($white, .15), $angle: 45deg) { 44 | background-image: linear-gradient($angle, $color 25%, transparent 25%, transparent 50%, $color 50%, $color 75%, transparent 75%, transparent); 45 | } 46 | -------------------------------------------------------------------------------- /src/components/ColorPicker/Checkboard.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 83 | 84 | 96 | -------------------------------------------------------------------------------- /src/sass/utilities/_text.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // 4 | // Text 5 | // 6 | 7 | .text-monospace { font-family: $font-family-monospace !important; } 8 | 9 | // Alignment 10 | 11 | .text-justify { text-align: justify !important; } 12 | .text-wrap { white-space: normal !important; } 13 | .text-nowrap { white-space: nowrap !important; } 14 | //.text-truncate { @include text-truncate; } 15 | 16 | // Responsive alignment 17 | 18 | @each $breakpoint in map-keys($grid-breakpoints) { 19 | @include media-breakpoint-up($breakpoint) { 20 | $infix: breakpoint-infix($breakpoint, $grid-breakpoints); 21 | 22 | .text#{$infix}-left { text-align: left !important; } 23 | .text#{$infix}-right { text-align: right !important; } 24 | .text#{$infix}-center { text-align: center !important; } 25 | } 26 | } 27 | 28 | // Transformation 29 | 30 | .text-lowercase { text-transform: lowercase !important; } 31 | .text-uppercase { text-transform: uppercase !important; } 32 | .text-capitalize { text-transform: capitalize !important; } 33 | 34 | // Weight and italics 35 | 36 | .font-weight-light { font-weight: $font-weight-light !important; } 37 | .font-weight-lighter { font-weight: $font-weight-lighter !important; } 38 | .font-weight-normal { font-weight: $font-weight-normal !important; } 39 | .font-weight-bold { font-weight: $font-weight-bold !important; } 40 | .font-weight-bolder { font-weight: $font-weight-bolder !important; } 41 | .font-italic { font-style: italic !important; } 42 | 43 | // Contextual colors 44 | 45 | .text-white { color: $white !important; } 46 | 47 | @each $color, $value in $theme-colors { 48 | @include text-emphasis-variant(".text-#{$color}", $value); 49 | } 50 | 51 | .text-body { color: $body-color !important; } 52 | .text-muted { color: $text-muted !important; } 53 | 54 | .text-black-50 { color: rgba($black, .5) !important; } 55 | .text-white-50 { color: rgba($white, .5) !important; } 56 | 57 | // Misc 58 | 59 | //.text-hide { 60 | // @include text-hide($ignore-warning: true); 61 | //} 62 | 63 | .text-decoration-none { text-decoration: none !important; } 64 | 65 | .text-break { 66 | word-break: break-word !important; // IE & < Edge 18 67 | overflow-wrap: break-word !important; 68 | } 69 | 70 | // Reset 71 | 72 | .text-reset { color: inherit !important; } 73 | -------------------------------------------------------------------------------- /src/sass/utilities/_spacing.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important 2 | 3 | // Margin and Padding 4 | 5 | @each $breakpoint in map-keys($grid-breakpoints) { 6 | @include media-breakpoint-up($breakpoint) { 7 | $infix: breakpoint-infix($breakpoint, $grid-breakpoints); 8 | 9 | @each $prop, $abbrev in (margin: m, padding: p) { 10 | @each $size, $length in $spacers { 11 | .#{$abbrev}#{$infix}-#{$size} { #{$prop}: $length !important; } 12 | .#{$abbrev}t#{$infix}-#{$size}, 13 | .#{$abbrev}y#{$infix}-#{$size} { 14 | #{$prop}-top: $length !important; 15 | } 16 | .#{$abbrev}r#{$infix}-#{$size}, 17 | .#{$abbrev}x#{$infix}-#{$size} { 18 | #{$prop}-right: $length !important; 19 | } 20 | .#{$abbrev}b#{$infix}-#{$size}, 21 | .#{$abbrev}y#{$infix}-#{$size} { 22 | #{$prop}-bottom: $length !important; 23 | } 24 | .#{$abbrev}l#{$infix}-#{$size}, 25 | .#{$abbrev}x#{$infix}-#{$size} { 26 | #{$prop}-left: $length !important; 27 | } 28 | } 29 | } 30 | 31 | // Negative margins (e.g., where `.mb-n1` is negative version of `.mb-1`) 32 | @each $size, $length in $spacers { 33 | @if $size != 0 { 34 | .m#{$infix}-n#{$size} { margin: -$length !important; } 35 | .mt#{$infix}-n#{$size}, 36 | .my#{$infix}-n#{$size} { 37 | margin-top: -$length !important; 38 | } 39 | .mr#{$infix}-n#{$size}, 40 | .mx#{$infix}-n#{$size} { 41 | margin-right: -$length !important; 42 | } 43 | .mb#{$infix}-n#{$size}, 44 | .my#{$infix}-n#{$size} { 45 | margin-bottom: -$length !important; 46 | } 47 | .ml#{$infix}-n#{$size}, 48 | .mx#{$infix}-n#{$size} { 49 | margin-left: -$length !important; 50 | } 51 | } 52 | } 53 | 54 | // Some special margin utils 55 | .m#{$infix}-auto { margin: auto !important; } 56 | .mt#{$infix}-auto, 57 | .my#{$infix}-auto { 58 | margin-top: auto !important; 59 | } 60 | .mr#{$infix}-auto, 61 | .mx#{$infix}-auto { 62 | margin-right: auto !important; 63 | } 64 | .mb#{$infix}-auto, 65 | .my#{$infix}-auto { 66 | margin-bottom: auto !important; 67 | } 68 | .ml#{$infix}-auto, 69 | .mx#{$infix}-auto { 70 | margin-left: auto !important; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/sass/_type.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable declaration-no-important, selector-list-comma-newline-after 2 | 3 | // 4 | // Headings 5 | // 6 | 7 | h1, h2, h3, h4, h5, h6, 8 | .h1, .h2, .h3, .h4, .h5, .h6 { 9 | margin-bottom: $headings-margin-bottom; 10 | font-family: $headings-font-family; 11 | font-weight: $headings-font-weight; 12 | line-height: $headings-line-height; 13 | color: $headings-color; 14 | } 15 | 16 | h1, .h1 { @include font-size($h1-font-size); } 17 | h2, .h2 { @include font-size($h2-font-size); } 18 | h3, .h3 { @include font-size($h3-font-size); } 19 | h4, .h4 { @include font-size($h4-font-size); } 20 | h5, .h5 { @include font-size($h5-font-size); } 21 | h6, .h6 { @include font-size($h6-font-size); } 22 | 23 | .lead { 24 | @include font-size($lead-font-size); 25 | font-weight: $lead-font-weight; 26 | } 27 | 28 | // Type display classes 29 | .display-1 { 30 | @include font-size($display1-size); 31 | font-weight: $display1-weight; 32 | line-height: $display-line-height; 33 | } 34 | .display-2 { 35 | @include font-size($display2-size); 36 | font-weight: $display2-weight; 37 | line-height: $display-line-height; 38 | } 39 | .display-3 { 40 | @include font-size($display3-size); 41 | font-weight: $display3-weight; 42 | line-height: $display-line-height; 43 | } 44 | .display-4 { 45 | @include font-size($display4-size); 46 | font-weight: $display4-weight; 47 | line-height: $display-line-height; 48 | } 49 | 50 | 51 | // 52 | // Horizontal rules 53 | // 54 | 55 | hr { 56 | margin-top: $hr-margin-y; 57 | margin-bottom: $hr-margin-y; 58 | border: 0; 59 | border-top: $hr-border-width solid $hr-border-color; 60 | } 61 | 62 | 63 | // 64 | // Emphasis 65 | // 66 | 67 | small, 68 | .small { 69 | @include font-size($small-font-size); 70 | font-weight: $font-weight-normal; 71 | } 72 | 73 | mark, 74 | .mark { 75 | padding: $mark-padding; 76 | background-color: $mark-bg; 77 | } 78 | 79 | 80 | // 81 | // Lists 82 | // 83 | 84 | .list-unstyled { 85 | @include list-unstyled; 86 | } 87 | 88 | // Inline turns list items into inline-block 89 | .list-inline { 90 | @include list-unstyled; 91 | } 92 | .list-inline-item { 93 | display: inline-block; 94 | 95 | &:not(:last-child) { 96 | margin-right: $list-inline-padding; 97 | } 98 | } 99 | 100 | 101 | // 102 | // Misc 103 | // 104 | 105 | // Builds on `abbr` 106 | .initialism { 107 | @include font-size(90%); 108 | text-transform: uppercase; 109 | } 110 | 111 | // Blockquotes 112 | .blockquote { 113 | margin-bottom: $spacer; 114 | @include font-size($blockquote-font-size); 115 | } 116 | 117 | .blockquote-footer { 118 | display: block; 119 | @include font-size($blockquote-small-font-size); 120 | color: $blockquote-small-color; 121 | 122 | &::before { 123 | content: "\2014\00A0"; // em dash, nbsp 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/sass/_nav.scss: -------------------------------------------------------------------------------- 1 | // Base class 2 | // 3 | // Kickstart any navigation component with a set of style resets. Works with 4 | // `