diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 54abc3f542..d1c86d33d6 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -134,6 +134,7 @@ services: volumes: - "./ui/src:/alephui/src:cached" - "./ui/package.json:/alephui/package.json:cached" + - "./ui/craco.config.js:/alephui/craco.config.js:cached" - "./ui/node_modules:/alephui/node_modules:delegated" environment: PORT: "8080" diff --git a/ui/Dockerfile b/ui/Dockerfile index 91a79cbbf9..37a696fd21 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -12,6 +12,7 @@ COPY .npmrc /alephui/.npmrc COPY .prettierrc /alephui/.prettierrc COPY tsconfig.json /alephui/tsconfig.json COPY package.json /alephui +COPY craco.config.js /alephui RUN npm install RUN cp -R /alephui/node_modules/ /node_modules diff --git a/ui/craco.config.js b/ui/craco.config.js new file mode 100644 index 0000000000..32464d2c89 --- /dev/null +++ b/ui/craco.config.js @@ -0,0 +1,81 @@ +const { + IconSvgPaths16, + IconSvgPaths20, + iconNameToPathsRecordKey, +} = require('@blueprintjs/icons'); +const sass = require('sass'); + +const findFill = (map) => { + for (let i = 0; i < map.getLength(); i++) { + const key = map.getKey(i).getValue(); + const value = map.getValue(i); + + if (key === 'fill' && value instanceof sass.types.Color) { + return `rgba(${value.getR()}, ${value.getG()}, ${value.getB()}, ${value.getA()})`; + } + + if (value instanceof sass.types.Map) { + const recursiveFill = findFill(value); + + if (recursiveFill !== null) { + return recursiveFill; + } + } + } + + return null; +}; + +// Blueprint internally uses a custom Sass function `svg-icon` [1] to inline +// icon SVGs to data URIs. In order to be able to build Blueprint from the SCSS +// source, we need to provide a reimplementation of this function to the Sass +// compiler. The implementation used by Blueprint [2] won’t work out of the box, +// as it relies on the raw SVG files which are not exported by `@blueprint/icons`. +// For this reason, the `svgIcon` function is implemented differently, using the +// SVG paths exported by `@blueprint/icons`. +// +// [1]: https://sass-lang.com/documentation/js-api/interfaces/Options#functions +// [2]: https://github.com/palantir/blueprint/blob/develop/packages/core/scripts/sass-custom-functions.js + +const svgIcon = (path, selectors) => { + // Parse a string "16px/chevron-right.svg" into size (16) and a name (chevron-right). + const { size, name } = path + .getValue() + .match(/^(?16|20)px\/(?[a-z\-]+)\.svg$/)?.groups; + + // The `selectors` argument is a nested map that represents CSS rules. For example: + // (path: (fill: '#000')). We try to find a `fill` property in the list and use that + // in the SVG generated below. + const fill = findFill(selectors); + + // Get the SVG path for requested icon size and name. + const paths = size === '16' ? IconSvgPaths16 : IconSvgPaths20; + const path = paths[iconNameToPathsRecordKey(name)]; + + // Assemble an icon SVG element and encode it as a data URI. + const svg = + `` + + `` + + ``; + + const value = `url('data:image/svg+xml,${encodeURIComponent(svg)}')`; + + return new sass.types.String(value); +}; + +module.exports = { + style: { + sass: { + implementation: sass, + loaderOptions: (options) => ({ + ...options, + implementation: sass, + sassOptions: { + functions: { + 'svg-icon($path, $selectors: null)': svgIcon, + }, + }, + }), + }, + }, +}; diff --git a/ui/package.json b/ui/package.json index 7227e597c9..0e944a4119 100644 --- a/ui/package.json +++ b/ui/package.json @@ -10,6 +10,7 @@ "@blueprintjs/popover2": "^1.5.1", "@blueprintjs/select": "^4.5.3", "@blueprintjs/table": "^4.6.0", + "@craco/craco": "^7.0.0-alpha.7", "@formatjs/cli": "^5.0.2", "@formatjs/intl-locale": "^3.0.3", "@formatjs/intl-pluralrules": "^5.0.2", @@ -59,7 +60,7 @@ "redux-act": "^1.7.4", "redux-thunk": "^2.3.0", "rehype-raw": "^6.0.0", - "sass": "^1.54.4", + "sass": "^1.54.5", "stream": "npm:stream-browserify@^3.0.0", "truncate": "^3.0.0", "typescript": "^4.0.2", @@ -67,11 +68,11 @@ "yaml": "^2.1.1" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", + "start": "craco start", + "build": "craco build", "lint": "eslint --ext js,jsx,ts,tsx src", - "test": "react-scripts test", - "eject": "react-scripts eject", + "test": "craco test", + "eject": "craco eject", "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx,css,scss}'", "format:check": "prettier --check 'src/**/*.{js,jsx,ts,tsx,css,scss}'", "translate": "npm run messages && npm run compile-translations && npm run concat-translations", diff --git a/ui/src/app/App.scss b/ui/src/app/App.scss index 542e7d71f9..f5996f7dc1 100644 --- a/ui/src/app/App.scss +++ b/ui/src/app/App.scss @@ -1,26 +1,3 @@ -// hack hack hack -// https://github.com/palantir/blueprint/issues/2976#issuecomment-479231949 -$svg-icon-map: ( - '16px/small-minus.svg': - "path fill-rule='evenodd' clip-rule='evenodd' d='M11 7H5c-.55 0-1 .45-1 1s.45 1 1 1h6c.55 0 1-.45 1-1s-.45-1-1-1z' fill='%23fff'/", - '16px/small-tick.svg': - "path fill-rule='evenodd' clip-rule='evenodd' d='M12 5c-.28 0-.53.11-.71.29L7 9.59l-2.29-2.3a1.003 1.003 0 0 0-1.42 1.42l3 3c.18.18.43.29.71.29s.53-.11.71-.29l5-5A1.003 1.003 0 0 0 12 5z' fill='%23fff'/", - // '16px/chevron-right.svg': "path fill-rule='evenodd' clip-rule='evenodd' d='M10.71 7.29l-4-4a1.003 1.003 0 0 0-1.42 1.42L8.59 8 5.3 11.29c-.19.18-.3.43-.3.71a1.003 1.003 0 0 0 1.71.71l4-4c.18-.18.29-.43.29-.71 0-.28-.11-.53-.29-.71z' fill='%235C7080'/", - '16px/chevron-right.svg': - "path fill-rule='evenodd' clip-rule='evenodd' d='M10.71 7.29l-4-4a1.003 1.003 0 0 0-1.42 1.42L8.59 8 5.3 11.29c-.19.18-.3.43-.3.71a1.003 1.003 0 0 0 1.71.71l4-4c.18-.18.29-.43.29-.71 0-.28-.11-.53-.29-.71z' fill='%23ccc'/", - '16px/more.svg': - "g fill='%235C7080'%3E%3Ccircle cx='2' cy='8.03' r='2'/%3E%3Ccircle cx='14' cy='8.03' r='2'/%3E%3Ccircle cx='8' cy='8.03' r='2'/%3E%3C/g", -); - -@function svg-icon($inline-svg, $fill-color) { - @return url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3C" + map-get( - $svg-icon-map, - $inline-svg - ) + '%3E%3C/svg%3E'); -} - -$icon-font-path: '~@blueprintjs/icons/resources/icons'; - @import '~@blueprintjs/core/src/blueprint.scss'; @import '~@blueprintjs/table/lib/css/table.css'; @import '~@blueprintjs/icons/src/blueprint-icons.scss';