From a6cf7c1b16245987d072cc6115d25fc729239977 Mon Sep 17 00:00:00 2001 From: Dan Elbert Date: Mon, 10 Sep 2018 17:25:36 -0500 Subject: [PATCH] icons --- app/javascript/components/AppIcon.vue | 279 ++++++-------------- app/javascript/components/AppIconicIcon.vue | 151 +++++++++++ app/javascript/packs/application.js | 6 + app/javascript/styles/_iconic.scss | 14 + config/webpack/loaders/iconic_svg_loader.js | 81 ++++++ config/webpack/loaders/svg.js | 4 +- config/webpack/loaders/svg_loader.js | 27 -- package.json | 2 + yarn.lock | 100 ++++++- 9 files changed, 431 insertions(+), 233 deletions(-) create mode 100644 app/javascript/components/AppIconicIcon.vue create mode 100644 config/webpack/loaders/iconic_svg_loader.js delete mode 100644 config/webpack/loaders/svg_loader.js diff --git a/app/javascript/components/AppIcon.vue b/app/javascript/components/AppIcon.vue index f28bd50..003784b 100644 --- a/app/javascript/components/AppIcon.vue +++ b/app/javascript/components/AppIcon.vue @@ -1,190 +1,49 @@ \ No newline at end of file diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 2e64fa3..895c4ef 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -1,10 +1,12 @@ import '../styles'; +import "vue-resize/dist/vue-resize"; import Vue from 'vue' import { sync } from 'vuex-router-sync'; import { swInit } from "../lib/ServiceWorker"; import responsiveSync from "../lib/VuexResponsiveSync"; import VueProgressBar from "vue-progressbar"; +import VueResize from "vue-resize"; import config from '../config'; import store from '../store'; import router from '../router'; @@ -18,6 +20,7 @@ import AppDatePicker from "../components/AppDatePicker"; import AppDropdown from "../components/AppDropdown"; import AppExpandTransition from "../components/AppExpandTransition"; import AppIcon from "../components/AppIcon"; +import AppIconicIcon from "../components/AppIconicIcon"; import AppModal from "../components/AppModal"; import AppNavbar from "../components/AppNavbar"; import AppPager from "../components/AppPager"; @@ -33,6 +36,7 @@ Vue.component("AppDatePicker", AppDatePicker); Vue.component("AppDropdown", AppDropdown); Vue.component("AppExpandTransition", AppExpandTransition); Vue.component("AppIcon", AppIcon); +Vue.component("AppIconicIcon", AppIconicIcon); Vue.component("AppModal", AppModal); Vue.component("AppNavbar", AppNavbar); Vue.component("AppPager", AppPager); @@ -56,6 +60,8 @@ Vue.use(VueProgressBar, { // inverse: false }); +Vue.use(VueResize); + sync(store, router); swInit(store); responsiveSync(store); diff --git a/app/javascript/styles/_iconic.scss b/app/javascript/styles/_iconic.scss index 984e94c..5d489ef 100644 --- a/app/javascript/styles/_iconic.scss +++ b/app/javascript/styles/_iconic.scss @@ -1,3 +1,17 @@ +/* Iconic Responsive Support Styles */ +.iconic-property-fill, .iconic-property-text {stroke: none !important;} +.iconic-property-stroke {fill: none !important;} +svg.iconic.iconic-fluid {height:100% !important;width:100% !important;} +svg.iconic.iconic-sm:not(.iconic-size-md):not(.iconic-size-lg), svg.iconic.iconic-size-sm{width:16px;height:16px;} +svg.iconic.iconic-md:not(.iconic-size-sm):not(.iconic-size-lg), svg.iconic.iconic-size-md{width:32px;height:32px;} +svg.iconic.iconic-lg:not(.iconic-size-sm):not(.iconic-size-md), svg.iconic.iconic-size-lg{width:128px;height:128px;} +svg.iconic-sm > g.iconic-md, svg.iconic-sm > g.iconic-lg, svg.iconic-md > g.iconic-sm, svg.iconic-md > g.iconic-lg, svg.iconic-lg > g.iconic-sm, svg.iconic-lg > g.iconic-md {display: none;} +svg.iconic.iconic-icon-sm > g.iconic-lg, svg.iconic.iconic-icon-md > g.iconic-lg {display:none;} +svg.iconic-sm:not(.iconic-icon-md):not(.iconic-icon-lg) > g.iconic-sm, svg.iconic-md.iconic-icon-sm > g.iconic-sm, svg.iconic-lg.iconic-icon-sm > g.iconic-sm {display:inline;} +svg.iconic-md:not(.iconic-icon-sm):not(.iconic-icon-lg) > g.iconic-md, svg.iconic-sm.iconic-icon-md > g.iconic-md, svg.iconic-lg.iconic-icon-md > g.iconic-md {display:inline;} +svg.iconic-lg:not(.iconic-icon-sm):not(.iconic-icon-md) > g.iconic-lg, svg.iconic-sm.iconic-icon-lg > g.iconic-lg, svg.iconic-md.iconic-icon-lg > g.iconic-lg {display:inline;} + + @mixin iconic-color($color) { fill: $color; stroke: $color; diff --git a/config/webpack/loaders/iconic_svg_loader.js b/config/webpack/loaders/iconic_svg_loader.js new file mode 100644 index 0000000..ece1ca5 --- /dev/null +++ b/config/webpack/loaders/iconic_svg_loader.js @@ -0,0 +1,81 @@ +const cheerio = require('cheerio'); + +const iriElementsAndProperties = { + 'clipPath': ['clip-path'], + 'color-profile': ['color-profile'], + 'cursor': ['cursor'], + 'filter': ['filter'], + 'linearGradient': ['fill', 'stroke'], + 'marker': ['marker', 'marker-start', 'marker-mid', 'marker-end'], + 'mask': ['mask'], + 'pattern': ['fill', 'stroke'], + 'radialGradient': ['fill', 'stroke'] +}; + +module.exports = function(content) { + this.cacheable && this.cacheable(); + + const $ = cheerio.load(content, { + lowerCaseTags: false + }); + + let idReplacements = []; + + for(let element in iriElementsAndProperties) { + let properties = iriElementsAndProperties[element]; + + $(`svg defs ${element}[id]`).each(function() { + const $elem = $(this); + const id = $elem.attr("id"); + const newId = `__ICONIC_SVG_REPLACEMENT_${idReplacements.length}`; + idReplacements.push(newId); + + $elem.attr("id", newId); + + for (let property of properties) { + $(`svg *[${property}="url(#${id})"]`).each(function() { + const $elem = $(this); + $elem.attr(property, `url(#${newId})`); + }) + } + }) + } + + const $svg = $("svg"); + let iconName = $svg.attr("data-icon"); + + if (!iconName) { + const match = $svg.attr("class").match(/iconic-([a-z\-]+)/); + if (match) { + $svg.attr("data-icon", match[1]); + } else { + throw "Can't parse class into data-icon"; + } + } + + const scriptBlocks = []; + + // Find then prune the scripts + $("svg script").each(function() { + const $script = $(this); + const scriptType = $script.attr("type"); + if (!scriptType || scriptType === 'application/ecmascript' || scriptType === 'application/javascript') { + scriptBlocks.push($script.html()); + } + }); + + $("svg script").remove(); + + + const attributes = $svg.get(0).attribs; + const svgMarkup = $("svg").html(); + + + return `module.exports = ${JSON.stringify({ + attributes: attributes, + content: svgMarkup, + idReplacements: idReplacements, + scriptBlocks: scriptBlocks + })}`; + +}; \ No newline at end of file diff --git a/config/webpack/loaders/svg.js b/config/webpack/loaders/svg.js index b1da427..cdaf644 100644 --- a/config/webpack/loaders/svg.js +++ b/config/webpack/loaders/svg.js @@ -1,9 +1,9 @@ const path = require("path"); module.exports = { - test: /\.svg$/, + test: /\/iconic\/svg\/.*\.svg$/, use: [{ - loader: path.join(__dirname, 'svg_loader.js') + loader: path.join(__dirname, 'iconic_svg_loader.js') }] }; diff --git a/config/webpack/loaders/svg_loader.js b/config/webpack/loaders/svg_loader.js deleted file mode 100644 index 619b321..0000000 --- a/config/webpack/loaders/svg_loader.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = function(content) { - this.cacheable && this.cacheable(); - - var match = content.match(/]+)+>([\s\S]+)<\/svg>/i); - var attrs = {}; - - if (match) { - attrs = match[1]; - if (attrs) { - attrs = attrs.match(/([\w-:]+)(=)?("[^<>"]*"|'[^<>']*'|[\w-:]+)/g) - .reduce(function(obj, attr){ - var split = attr.split('='); - var name = split[0]; - var value = true; - if (split && split[1]) { - value = split[1].replace(/['"]/g, ''); - } - obj[name] = value; - return obj; - }, {}) - } - - content = match[2] || ''; - }; - - return "module.exports = " + JSON.stringify({attributes: attrs, content: content}); -}; \ No newline at end of file diff --git a/package.json b/package.json index f4d2a59..be4bc09 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "autosize": "^4.0.1", "bulma": "^0.7.1", "caniuse-lite": "^1.0.30000815", + "cheerio": "^1.0.0-rc.2", "css-loader": "^0.28.11", "lodash": "^4.17.5", "svg-loader": "^0.0.2", @@ -11,6 +12,7 @@ "vue": "^2.5.16", "vue-loader": "^14.2.2", "vue-progressbar": "^0.7.4", + "vue-resize": "^0.4.4", "vue-router": "^3.0.1", "vue-template-compiler": "^2.5.16", "vuex": "^3.0.1", diff --git a/yarn.lock b/yarn.lock index 23991d1..ec11ec3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32,6 +32,10 @@ webpack "^3.12.0" webpack-manifest-plugin "^1.3.2" +"@types/node@*": + version "10.9.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -872,6 +876,10 @@ bonjour@^3.5.0: multicast-dns "^6.0.1" multicast-dns-service-types "^1.1.0" +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1130,6 +1138,17 @@ chalk@^2.0.1, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + chokidar@^2.0.0, chokidar@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" @@ -1522,6 +1541,15 @@ css-loader@^0.28.11: postcss-value-parser "^3.3.0" source-list-map "^2.0.0" +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + css-selector-tokenizer@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" @@ -1534,6 +1562,10 @@ css-unit-converter@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" +css-what@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + cssesc@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" @@ -1746,10 +1778,45 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" +dom-serializer@0, dom-serializer@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + dependencies: + dom-serializer "0" + domelementtype "1" + duplexify@^3.4.2, duplexify@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410" @@ -1809,6 +1876,10 @@ enhanced-resolve@^3.4.0: object-assign "^4.0.1" tapable "^0.2.7" +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -2530,6 +2601,17 @@ html-entities@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" +htmlparser2@^3.9.1: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -3180,7 +3262,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10: +"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" @@ -3647,6 +3729,12 @@ npm-run-path@^2.0.0: gauge "~2.7.3" set-blocking "~2.0.0" +nth-check@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" @@ -3842,6 +3930,12 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + dependencies: + "@types/node" "*" + parseurl@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" @@ -5796,6 +5890,10 @@ vue-progressbar@^0.7.4: version "0.7.5" resolved "https://registry.yarnpkg.com/vue-progressbar/-/vue-progressbar-0.7.5.tgz#414730892252b1e45582d4979dec93038e007f79" +vue-resize@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-0.4.4.tgz#dee9b8dd1b189e7e3f6ec47f86c60694a241bb17" + vue-router@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"