@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
{ |
||||
"extends": "airbnb", |
||||
"parser": "babel-eslint", |
||||
|
||||
"plugins": [ |
||||
"react" |
||||
], |
||||
|
||||
"env": { |
||||
"browser": true, |
||||
"node": true, |
||||
"mocha": true, |
||||
"es6": true |
||||
}, |
||||
|
||||
"rules": { |
||||
"jsx-a11y/anchor-is-valid": [ "error", { |
||||
"components": [ "Link" ], |
||||
"specialLink": [ "to", "hrefLeft", "hrefRight" ], |
||||
"aspects": [ "noHref", "invalidHref", "preferButton" ] |
||||
}], |
||||
"max-len": ["error", {"code": 120, "ignoreRegExpLiterals": true, "tabWidth": 2}], |
||||
"no-console": [1], |
||||
"linebreak-style": "off", |
||||
"react/jsx-one-expression-per-line": [0, { "allow": "literal" }] |
||||
} |
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. |
||||
|
||||
# dependencies |
||||
/node_modules |
||||
|
||||
# testing |
||||
/coverage |
||||
|
||||
# production |
||||
/build |
||||
|
||||
# misc |
||||
.DS_Store |
||||
.env.local |
||||
.env.development.local |
||||
.env.test.local |
||||
.env.production.local |
||||
|
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
'use strict'; |
||||
|
||||
const fs = require('fs'); |
||||
const path = require('path'); |
||||
const paths = require('./paths'); |
||||
|
||||
// Make sure that including paths.js after env.js will read .env variables.
|
||||
delete require.cache[require.resolve('./paths')]; |
||||
|
||||
const NODE_ENV = process.env.NODE_ENV; |
||||
if (!NODE_ENV) { |
||||
throw new Error( |
||||
'The NODE_ENV environment variable is required but was not specified.' |
||||
); |
||||
} |
||||
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
var dotenvFiles = [ |
||||
`${paths.dotenv}.${NODE_ENV}.local`, |
||||
`${paths.dotenv}.${NODE_ENV}`, |
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`, |
||||
paths.dotenv, |
||||
].filter(Boolean); |
||||
|
||||
// Load environment variables from .env* files. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set. Variable expansion is supported in .env files.
|
||||
// https://github.com/motdotla/dotenv
|
||||
// https://github.com/motdotla/dotenv-expand
|
||||
dotenvFiles.forEach(dotenvFile => { |
||||
if (fs.existsSync(dotenvFile)) { |
||||
require('dotenv-expand')( |
||||
require('dotenv').config({ |
||||
path: dotenvFile, |
||||
}) |
||||
); |
||||
} |
||||
}); |
||||
|
||||
// We support resolving modules according to `NODE_PATH`.
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
// https://github.com/facebook/create-react-app/issues/253.
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
|
||||
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd()); |
||||
process.env.NODE_PATH = (process.env.NODE_PATH || '') |
||||
.split(path.delimiter) |
||||
.filter(folder => folder && !path.isAbsolute(folder)) |
||||
.map(folder => path.resolve(appDirectory, folder)) |
||||
.join(path.delimiter); |
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in Webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i; |
||||
|
||||
function getClientEnvironment(publicUrl) { |
||||
const raw = Object.keys(process.env) |
||||
.filter(key => REACT_APP.test(key)) |
||||
.reduce( |
||||
(env, key) => { |
||||
env[key] = process.env[key]; |
||||
return env; |
||||
}, |
||||
{ |
||||
// Useful for determining whether we’re running in production mode.
|
||||
// Most importantly, it switches React into the correct mode.
|
||||
NODE_ENV: process.env.NODE_ENV || 'development', |
||||
// Useful for resolving the correct path to static assets in `public`.
|
||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl, |
||||
} |
||||
); |
||||
// Stringify all values so we can feed into Webpack DefinePlugin
|
||||
const stringified = { |
||||
'process.env': Object.keys(raw).reduce((env, key) => { |
||||
env[key] = JSON.stringify(raw[key]); |
||||
return env; |
||||
}, {}), |
||||
}; |
||||
|
||||
return { raw, stringified }; |
||||
} |
||||
|
||||
module.exports = getClientEnvironment; |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
'use strict'; |
||||
|
||||
// This is a custom Jest transformer turning style imports into empty objects.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = { |
||||
process() { |
||||
return 'module.exports = {};'; |
||||
}, |
||||
getCacheKey() { |
||||
// The output is always the same.
|
||||
return 'cssTransform'; |
||||
}, |
||||
}; |
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
'use strict'; |
||||
|
||||
const path = require('path'); |
||||
|
||||
// This is a custom Jest transformer turning file imports into filenames.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = { |
||||
process(src, filename) { |
||||
const assetFilename = JSON.stringify(path.basename(filename)); |
||||
|
||||
if (filename.match(/\.svg$/)) { |
||||
return `module.exports = {
|
||||
__esModule: true, |
||||
default: ${assetFilename}, |
||||
ReactComponent: (props) => ({ |
||||
$$typeof: Symbol.for('react.element'), |
||||
type: 'svg', |
||||
ref: null, |
||||
key: null, |
||||
props: Object.assign({}, props, { |
||||
children: ${assetFilename} |
||||
}) |
||||
}), |
||||
};`;
|
||||
} |
||||
|
||||
return `module.exports = ${assetFilename};`; |
||||
}, |
||||
}; |
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
'use strict'; |
||||
|
||||
const path = require('path'); |
||||
const fs = require('fs'); |
||||
const url = require('url'); |
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebook/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd()); |
||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath); |
||||
|
||||
const envPublicUrl = process.env.PUBLIC_URL; |
||||
|
||||
function ensureSlash(inputPath, needsSlash) { |
||||
const hasSlash = inputPath.endsWith('/'); |
||||
if (hasSlash && !needsSlash) { |
||||
return inputPath.substr(0, inputPath.length - 1); |
||||
} else if (!hasSlash && needsSlash) { |
||||
return `${inputPath}/`; |
||||
} else { |
||||
return inputPath; |
||||
} |
||||
} |
||||
|
||||
const getPublicUrl = appPackageJson => |
||||
envPublicUrl || require(appPackageJson).homepage; |
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// Webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
function getServedPath(appPackageJson) { |
||||
const publicUrl = getPublicUrl(appPackageJson); |
||||
const servedUrl = |
||||
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/'); |
||||
return ensureSlash(servedUrl, true); |
||||
} |
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = { |
||||
dotenv: resolveApp('.env'), |
||||
appPath: resolveApp('.'), |
||||
appBuild: resolveApp('build'), |
||||
appPublic: resolveApp('public'), |
||||
appHtml: resolveApp('public/index.html'), |
||||
appIndexJs: resolveApp('src/index.jsx'), |
||||
appPackageJson: resolveApp('package.json'), |
||||
appSrc: resolveApp('src'), |
||||
yarnLockFile: resolveApp('yarn.lock'), |
||||
testsSetup: resolveApp('src/setupTests.js'), |
||||
proxySetup: resolveApp('src/setupProxy.js'), |
||||
appNodeModules: resolveApp('node_modules'), |
||||
publicUrl: getPublicUrl(resolveApp('package.json')), |
||||
servedPath: getServedPath(resolveApp('package.json')), |
||||
}; |
@ -0,0 +1,394 @@
@@ -0,0 +1,394 @@
|
||||
'use strict'; |
||||
|
||||
const path = require('path'); |
||||
const webpack = require('webpack'); |
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin'); |
||||
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); |
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); |
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); |
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); |
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); |
||||
const getClientEnvironment = require('./env'); |
||||
const paths = require('./paths'); |
||||
const ManifestPlugin = require('webpack-manifest-plugin'); |
||||
const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier'); |
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); |
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// In development, we always serve from the root. This makes config easier.
|
||||
const publicPath = '/'; |
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
|
||||
const publicUrl = ''; |
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl); |
||||
|
||||
// style files regexes
|
||||
const cssRegex = /\.css$/; |
||||
const cssModuleRegex = /\.module\.css$/; |
||||
const sassRegex = /\.(scss|sass)$/; |
||||
const sassModuleRegex = /\.module\.(scss|sass)$/; |
||||
|
||||
// common function to get style loaders
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => { |
||||
const loaders = [ |
||||
require.resolve('style-loader'), |
||||
{ |
||||
loader: require.resolve('css-loader'), |
||||
options: cssOptions, |
||||
}, |
||||
{ |
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'), |
||||
options: { |
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebook/create-react-app/issues/2677
|
||||
ident: 'postcss', |
||||
plugins: () => [ |
||||
require('postcss-flexbugs-fixes'), |
||||
require('postcss-preset-env')({ |
||||
autoprefixer: { |
||||
flexbox: 'no-2009', |
||||
}, |
||||
stage: 3, |
||||
}), |
||||
], |
||||
}, |
||||
}, |
||||
]; |
||||
if (preProcessor) { |
||||
loaders.push(require.resolve(preProcessor)); |
||||
} |
||||
return loaders; |
||||
}; |
||||
|
||||
// This is the development configuration.
|
||||
// It is focused on developer experience and fast rebuilds.
|
||||
// The production configuration is different and lives in a separate file.
|
||||
module.exports = { |
||||
mode: 'development', |
||||
// You may want 'eval' instead if you prefer to see the compiled output in DevTools.
|
||||
// See the discussion in https://github.com/facebook/create-react-app/issues/343
|
||||
devtool: 'cheap-module-source-map', |
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
entry: [ |
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'), |
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs, |
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
], |
||||
output: { |
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: true, |
||||
// This does not produce a real file. It's just the virtual path that is
|
||||
// served by WebpackDevServer in development. This is the JS bundle
|
||||
// containing code from all our entry points, and the Webpack runtime.
|
||||
filename: 'static/js/bundle.js', |
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: 'static/js/[name].chunk.js', |
||||
// This is the URL that app is served from. We use "/" in development.
|
||||
publicPath: publicPath, |
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: info => |
||||
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), |
||||
}, |
||||
optimization: { |
||||
// Automatically split vendor and commons
|
||||
// https://twitter.com/wSokra/status/969633336732905474
|
||||
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
|
||||
splitChunks: { |
||||
chunks: 'all', |
||||
name: false, |
||||
}, |
||||
// Keep the runtime chunk seperated to enable long term caching
|
||||
// https://twitter.com/wSokra/status/969679223278505985
|
||||
runtimeChunk: true, |
||||
}, |
||||
resolve: { |
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules'].concat( |
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean) |
||||
), |
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebook/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'], |
||||
alias: { |
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web', |
||||
}, |
||||
plugins: [ |
||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
||||
// guards against forgotten dependencies and such.
|
||||
PnpWebpackPlugin, |
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), |
||||
], |
||||
}, |
||||
resolveLoader: { |
||||
plugins: [ |
||||
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
|
||||
// from the current package.
|
||||
PnpWebpackPlugin.moduleLoader(module), |
||||
], |
||||
}, |
||||
module: { |
||||
strictExportPresence: true, |
||||
rules: [ |
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
{ parser: { requireEnsure: false } }, |
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{ |
||||
test: /\.(js|jsx)$/, |
||||
enforce: 'pre', |
||||
use: [ |
||||
{ |
||||
options: { |
||||
formatter: require.resolve('react-dev-utils/eslintFormatter'), |
||||
eslintPath: require.resolve('eslint'), |
||||
|
||||
}, |
||||
loader: require.resolve('eslint-loader'), |
||||
}, |
||||
], |
||||
include: paths.appSrc, |
||||
}, |
||||
{ |
||||
// `mjs` support is still in its infancy in the ecosystem, so we don't
|
||||
// support it.
|
||||
// Modules who define their `browser` or `module` key as `mjs` force
|
||||
// the use of this extension, so we need to tell webpack to fall back
|
||||
// to auto mode (ES Module interop, allows ESM to import CommonJS).
|
||||
test: /\.mjs$/, |
||||
include: /node_modules/, |
||||
type: 'javascript/auto', |
||||
}, |
||||
{ |
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [ |
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{ |
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], |
||||
loader: require.resolve('url-loader'), |
||||
options: { |
||||
limit: 10000, |
||||
name: 'static/media/[name].[hash:8].[ext]', |
||||
}, |
||||
}, |
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, and some ESnext features.
|
||||
{ |
||||
test: /\.(js|jsx)$/, |
||||
include: paths.appSrc, |
||||
loader: require.resolve('babel-loader'), |
||||
options: { |
||||
customize: require.resolve( |
||||
'babel-preset-react-app/webpack-overrides' |
||||
), |
||||
|
||||
plugins: [ |
||||
[ |
||||
require.resolve('babel-plugin-named-asset-import'), |
||||
{ |
||||
loaderMap: { |
||||
svg: { |
||||
ReactComponent: '@svgr/webpack?-prettier,-svgo![path]', |
||||
}, |
||||
}, |
||||
}, |
||||
], |
||||
], |
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
cacheDirectory: true, |
||||
// Don't waste time on Gzipping the cache
|
||||
cacheCompression: false, |
||||
}, |
||||
}, |
||||
// Process any JS outside of the app with Babel.
|
||||
// Unlike the application JS, we only compile the standard ES features.
|
||||
{ |
||||
test: /\.js$/, |
||||
exclude: /@babel(?:\/|\\{1,2})runtime/, |
||||
loader: require.resolve('babel-loader'), |
||||
options: { |
||||
babelrc: false, |
||||
configFile: false, |
||||
compact: false, |
||||
presets: [ |
||||
[ |
||||
require.resolve('babel-preset-react-app/dependencies'), |
||||
{ helpers: true }, |
||||
], |
||||
], |
||||
cacheDirectory: true, |
||||
// Don't waste time on Gzipping the cache
|
||||
cacheCompression: false, |
||||
|
||||
// If an error happens in a package, it's possible to be
|
||||
// because it was compiled. Thus, we don't want the browser
|
||||
// debugger to show the original code. Instead, the code
|
||||
// being evaluated would be much more helpful.
|
||||
sourceMaps: false, |
||||
}, |
||||
}, |
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use a plugin to extract that CSS to a file, but
|
||||
// in development "style" loader enables hot editing of CSS.
|
||||
// By default we support CSS Modules with the extension .module.css
|
||||
{ |
||||
test: cssRegex, |
||||
exclude: cssModuleRegex, |
||||
use: getStyleLoaders({ |
||||
importLoaders: 1, |
||||
}), |
||||
}, |
||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
||||
// using the extension .module.css
|
||||
{ |
||||
test: cssModuleRegex, |
||||
use: getStyleLoaders({ |
||||
importLoaders: 1, |
||||
modules: true, |
||||
getLocalIdent: getCSSModuleLocalIdent, |
||||
}), |
||||
}, |
||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
||||
// Chains the sass-loader with the css-loader and the style-loader
|
||||
// to immediately apply all styles to the DOM.
|
||||
// By default we support SASS Modules with the
|
||||
// extensions .module.scss or .module.sass
|
||||
{ |
||||
test: sassRegex, |
||||
exclude: sassModuleRegex, |
||||
use: getStyleLoaders({ importLoaders: 2 }, 'sass-loader'), |
||||
}, |
||||
// Adds support for CSS Modules, but using SASS
|
||||
// using the extension .module.scss or .module.sass
|
||||
{ |
||||
test: sassModuleRegex, |
||||
use: getStyleLoaders( |
||||
{ |
||||
importLoaders: 2, |
||||
modules: true, |
||||
getLocalIdent: getCSSModuleLocalIdent, |
||||
}, |
||||
'sass-loader' |
||||
), |
||||
}, |
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{ |
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|jsx)$/, /\.html$/, /\.json$/], |
||||
loader: require.resolve('file-loader'), |
||||
options: { |
||||
name: 'static/media/[name].[hash:8].[ext]', |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
], |
||||
}, |
||||
plugins: [ |
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({ |
||||
inject: true, |
||||
template: paths.appHtml, |
||||
}), |
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw), |
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
new ModuleNotFoundPlugin(paths.appPath), |
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
|
||||
new webpack.DefinePlugin(env.stringified), |
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
new webpack.HotModuleReplacementPlugin(), |
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebook/create-react-app/issues/240
|
||||
new CaseSensitivePathsPlugin(), |
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for Webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebook/create-react-app/issues/186
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules), |
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), |
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({ |
||||
fileName: 'asset-manifest.json', |
||||
publicPath: publicPath, |
||||
}), |
||||
], |
||||
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: { |
||||
dgram: 'empty', |
||||
fs: 'empty', |
||||
net: 'empty', |
||||
tls: 'empty', |
||||
child_process: 'empty', |
||||
}, |
||||
// Turn off performance processing because we utilize
|
||||
// our own hints via the FileSizeReporter
|
||||
performance: false, |
||||
}; |
@ -0,0 +1,500 @@
@@ -0,0 +1,500 @@
|
||||
'use strict'; |
||||
|
||||
const path = require('path'); |
||||
const webpack = require('webpack'); |
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin'); |
||||
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin'); |
||||
const TerserPlugin = require('terser-webpack-plugin'); |
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); |
||||
const safePostCssParser = require('postcss-safe-parser'); |
||||
const ManifestPlugin = require('webpack-manifest-plugin'); |
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); |
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); |
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); |
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); |
||||
const paths = require('./paths'); |
||||
const getClientEnvironment = require('./env'); |
||||
const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier'); |
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); |
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
const publicPath = paths.servedPath; |
||||
// Some apps do not use client-side routing with pushState.
|
||||
// For these, "homepage" can be set to "." to enable relative asset paths.
|
||||
const shouldUseRelativeAssetPaths = publicPath === './'; |
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; |
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
const publicUrl = publicPath.slice(0, -1); |
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl); |
||||
|
||||
// Assert this just to be safe.
|
||||
// Development builds of React are slow and not intended for production.
|
||||
if (env.stringified['process.env'].NODE_ENV !== '"production"') { |
||||
throw new Error('Production builds must have NODE_ENV=production.'); |
||||
} |
||||
|
||||
// style files regexes
|
||||
const cssRegex = /\.css$/; |
||||
const cssModuleRegex = /\.module\.css$/; |
||||
const sassRegex = /\.(scss|sass)$/; |
||||
const sassModuleRegex = /\.module\.(scss|sass)$/; |
||||
|
||||
// common function to get style loaders
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => { |
||||
const loaders = [ |
||||
{ |
||||
loader: MiniCssExtractPlugin.loader, |
||||
options: Object.assign( |
||||
{}, |
||||
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined |
||||
), |
||||
}, |
||||
{ |
||||
loader: require.resolve('css-loader'), |
||||
options: cssOptions, |
||||
}, |
||||
{ |
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'), |
||||
options: { |
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebook/create-react-app/issues/2677
|
||||
ident: 'postcss', |
||||
plugins: () => [ |
||||
require('postcss-flexbugs-fixes'), |
||||
require('postcss-preset-env')({ |
||||
autoprefixer: { |
||||
flexbox: 'no-2009', |
||||
}, |
||||
stage: 3, |
||||
}), |
||||
], |
||||
sourceMap: shouldUseSourceMap, |
||||
}, |
||||
}, |
||||
]; |
||||
if (preProcessor) { |
||||
loaders.push({ |
||||
loader: require.resolve(preProcessor), |
||||
options: { |
||||
sourceMap: shouldUseSourceMap, |
||||
}, |
||||
}); |
||||
} |
||||
return loaders; |
||||
}; |
||||
|
||||
// This is the production configuration.
|
||||
// It compiles slowly and is focused on producing a fast and minimal bundle.
|
||||
// The development configuration is different and lives in a separate file.
|
||||
module.exports = { |
||||
mode: 'production', |
||||
// Don't attempt to continue if there are any errors.
|
||||
bail: true, |
||||
// We generate sourcemaps in production. This is slow but gives good results.
|
||||
// You can exclude the *.map files from the build during deployment.
|
||||
devtool: shouldUseSourceMap ? 'source-map' : false, |
||||
// In production, we only want to load the app code.
|
||||
entry: [paths.appIndexJs], |
||||
output: { |
||||
// The build folder.
|
||||
path: paths.appBuild, |
||||
// Generated JS file names (with nested folders).
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// We don't currently advertise code splitting but Webpack supports it.
|
||||
filename: 'static/js/[name].[chunkhash:8].js', |
||||
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js', |
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: publicPath, |
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: info => |
||||
path |
||||
.relative(paths.appSrc, info.absoluteResourcePath) |
||||
.replace(/\\/g, '/'), |
||||
}, |
||||
optimization: { |
||||
minimizer: [ |
||||
new TerserPlugin({ |
||||
terserOptions: { |
||||
parse: { |
||||
// we want terser to parse ecma 8 code. However, we don't want it
|
||||
// to apply any minfication steps that turns valid ecma 5 code
|
||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
||||
// sections only apply transformations that are ecma 5 safe
|
||||
// https://github.com/facebook/create-react-app/pull/4234
|
||||
ecma: 8, |
||||
}, |
||||
compress: { |
||||
ecma: 5, |
||||
warnings: false, |
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false, |
||||
}, |
||||
mangle: { |
||||
safari10: true, |
||||
}, |
||||
output: { |
||||
ecma: 5, |
||||
comments: false, |
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebook/create-react-app/issues/2488
|
||||
ascii_only: true, |
||||
}, |
||||
}, |
||||
// Use multi-process parallel running to improve the build speed
|
||||
// Default number of concurrent runs: os.cpus().length - 1
|
||||
parallel: true, |
||||
// Enable file caching
|
||||
cache: true, |
||||
sourceMap: shouldUseSourceMap, |
||||
}), |
||||
new OptimizeCSSAssetsPlugin({ |
||||
cssProcessorOptions: { |
||||
parser: safePostCssParser, |
||||
map: { |
||||
// `inline: false` forces the sourcemap to be output into a
|
||||
// separate file
|
||||
inline: false, |
||||
// `annotation: true` appends the sourceMappingURL to the end of
|
||||
// the css file, helping the browser find the sourcemap
|
||||
annotation: true, |
||||
}, |
||||
}, |
||||
}), |
||||
], |
||||
// Automatically split vendor and commons
|
||||
// https://twitter.com/wSokra/status/969633336732905474
|
||||
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
|
||||
splitChunks: { |
||||
chunks: 'all', |
||||
name: false, |
||||
}, |
||||
// Keep the runtime chunk seperated to enable long term caching
|
||||
// https://twitter.com/wSokra/status/969679223278505985
|
||||
runtimeChunk: true, |
||||
}, |
||||
resolve: { |
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules'].concat( |
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean) |
||||
), |
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebook/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'], |
||||
alias: { |
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web', |
||||
}, |
||||
plugins: [ |
||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
||||
// guards against forgotten dependencies and such.
|
||||
PnpWebpackPlugin, |
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), |
||||
], |
||||
}, |
||||
resolveLoader: { |
||||
plugins: [ |
||||
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
|
||||
// from the current package.
|
||||
PnpWebpackPlugin.moduleLoader(module), |
||||
], |
||||
}, |
||||
module: { |
||||
strictExportPresence: true, |
||||
rules: [ |
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
{ parser: { requireEnsure: false } }, |
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{ |
||||
test: /\.(js|jsx)$/, |
||||
enforce: 'pre', |
||||
use: [ |
||||
{ |
||||
options: { |
||||
formatter: require.resolve('react-dev-utils/eslintFormatter'), |
||||
eslintPath: require.resolve('eslint'), |
||||
|
||||
}, |
||||
loader: require.resolve('eslint-loader'), |
||||
}, |
||||
], |
||||
include: paths.appSrc, |
||||
}, |
||||
{ |
||||
// `mjs` support is still in its infancy in the ecosystem, so we don't
|
||||
// support it.
|
||||
// Modules who define their `browser` or `module` key as `mjs` force
|
||||
// the use of this extension, so we need to tell webpack to fall back
|
||||
// to auto mode (ES Module interop, allows ESM to import CommonJS).
|
||||
test: /\.mjs$/, |
||||
include: /node_modules/, |
||||
type: 'javascript/auto', |
||||
}, |
||||
{ |
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [ |
||||
// "url" loader works just like "file" loader but it also embeds
|
||||
// assets smaller than specified size as data URLs to avoid requests.
|
||||
{ |
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], |
||||
loader: require.resolve('url-loader'), |
||||
options: { |
||||
limit: 10000, |
||||
name: 'static/media/[name].[hash:8].[ext]', |
||||
}, |
||||
}, |
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, and some ESnext features.
|
||||
{ |
||||
test: /\.(js|jsx)$/, |
||||
include: paths.appSrc, |
||||
|
||||
loader: require.resolve('babel-loader'), |
||||
options: { |
||||
customize: require.resolve( |
||||
'babel-preset-react-app/webpack-overrides' |
||||
), |
||||
|
||||
plugins: [ |
||||
[ |
||||
require.resolve('babel-plugin-named-asset-import'), |
||||
{ |
||||
loaderMap: { |
||||
svg: { |
||||
ReactComponent: '@svgr/webpack?-prettier,-svgo![path]', |
||||
}, |
||||
}, |
||||
}, |
||||
], |
||||
], |
||||
cacheDirectory: true, |
||||
// Save disk space when time isn't as important
|
||||
cacheCompression: true, |
||||
compact: true, |
||||
}, |
||||
}, |
||||
// Process any JS outside of the app with Babel.
|
||||
// Unlike the application JS, we only compile the standard ES features.
|
||||
{ |
||||
test: /\.js$/, |
||||
exclude: /@babel(?:\/|\\{1,2})runtime/, |
||||
loader: require.resolve('babel-loader'), |
||||
options: { |
||||
babelrc: false, |
||||
configFile: false, |
||||
compact: false, |
||||
presets: [ |
||||
[ |
||||
require.resolve('babel-preset-react-app/dependencies'), |
||||
{ helpers: true }, |
||||
], |
||||
], |
||||
cacheDirectory: true, |
||||
// Save disk space when time isn't as important
|
||||
cacheCompression: true, |
||||
|
||||
// If an error happens in a package, it's possible to be
|
||||
// because it was compiled. Thus, we don't want the browser
|
||||
// debugger to show the original code. Instead, the code
|
||||
// being evaluated would be much more helpful.
|
||||
sourceMaps: false, |
||||
}, |
||||
}, |
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// `MiniCSSExtractPlugin` extracts styles into CSS
|
||||
// files. If you use code splitting, async bundles will have their own separate CSS chunk file.
|
||||
// By default we support CSS Modules with the extension .module.css
|
||||
{ |
||||
test: cssRegex, |
||||
exclude: cssModuleRegex, |
||||
loader: getStyleLoaders({ |
||||
importLoaders: 1, |
||||
sourceMap: shouldUseSourceMap, |
||||
}), |
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true, |
||||
}, |
||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
||||
// using the extension .module.css
|
||||
{ |
||||
test: cssModuleRegex, |
||||
loader: getStyleLoaders({ |
||||
importLoaders: 1, |
||||
sourceMap: shouldUseSourceMap, |
||||
modules: true, |
||||
getLocalIdent: getCSSModuleLocalIdent, |
||||
}), |
||||
}, |
||||
// Opt-in support for SASS. The logic here is somewhat similar
|
||||
// as in the CSS routine, except that "sass-loader" runs first
|
||||
// to compile SASS files into CSS.
|
||||
// By default we support SASS Modules with the
|
||||
// extensions .module.scss or .module.sass
|
||||
{ |
||||
test: sassRegex, |
||||
exclude: sassModuleRegex, |
||||
loader: getStyleLoaders( |
||||
{ |
||||
importLoaders: 2, |
||||
sourceMap: shouldUseSourceMap, |
||||
}, |
||||
'sass-loader' |
||||
), |
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true, |
||||
}, |
||||
// Adds support for CSS Modules, but using SASS
|
||||
// using the extension .module.scss or .module.sass
|
||||
{ |
||||
test: sassModuleRegex, |
||||
loader: getStyleLoaders( |
||||
{ |
||||
importLoaders: 2, |
||||
sourceMap: shouldUseSourceMap, |
||||
modules: true, |
||||
getLocalIdent: getCSSModuleLocalIdent, |
||||
}, |
||||
'sass-loader' |
||||
), |
||||
}, |
||||
// "file" loader makes sure assets end up in the `build` folder.
|
||||
// When you `import` an asset, you get its filename.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{ |
||||
loader: require.resolve('file-loader'), |
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// it's runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|jsx)$/, /\.html$/, /\.json$/], |
||||
options: { |
||||
name: 'static/media/[name].[hash:8].[ext]', |
||||
}, |
||||
}, |
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
plugins: [ |
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({ |
||||
inject: true, |
||||
template: paths.appHtml, |
||||
minify: { |
||||
removeComments: true, |
||||
collapseWhitespace: true, |
||||
removeRedundantAttributes: true, |
||||
useShortDoctype: true, |
||||
removeEmptyAttributes: true, |
||||
removeStyleLinkTypeAttributes: true, |
||||
keepClosingSlash: true, |
||||
minifyJS: true, |
||||
minifyCSS: true, |
||||
minifyURLs: true, |
||||
}, |
||||
}), |
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]), |
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In production, it will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw), |
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
new ModuleNotFoundPlugin(paths.appPath), |
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV was set to production here.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified), |
||||
new MiniCssExtractPlugin({ |
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: 'static/css/[name].[contenthash:8].css', |
||||
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', |
||||
}), |
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({ |
||||
fileName: 'asset-manifest.json', |
||||
publicPath: publicPath, |
||||
}), |
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), |
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the Webpack build.
|
||||
new WorkboxWebpackPlugin.GenerateSW({ |
||||
clientsClaim: true, |
||||
exclude: [/\.map$/, /asset-manifest\.json$/], |
||||
importWorkboxFrom: 'cdn', |
||||
navigateFallback: publicUrl + '/index.html', |
||||
navigateFallbackBlacklist: [ |
||||
// Exclude URLs starting with /_, as they're likely an API call
|
||||
new RegExp('^/_'), |
||||
// Exclude URLs containing a dot, as they're likely a resource in
|
||||
// public/ and not a SPA route
|
||||
new RegExp('/[^/]+\\.[^/]+$'), |
||||
], |
||||
}), |
||||
], |
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: { |
||||
dgram: 'empty', |
||||
fs: 'empty', |
||||
net: 'empty', |
||||
tls: 'empty', |
||||
child_process: 'empty', |
||||
}, |
||||
// Turn off performance processing because we utilize
|
||||
// our own hints via the FileSizeReporter
|
||||
performance: false, |
||||
}; |
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
'use strict'; |
||||
|
||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware'); |
||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware'); |
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware'); |
||||
const ignoredFiles = require('react-dev-utils/ignoredFiles'); |
||||
const config = require('./webpack.config.dev'); |
||||
const paths = require('./paths'); |
||||
const fs = require('fs'); |
||||
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; |
||||
const host = process.env.HOST || '0.0.0.0'; |
||||
|
||||
module.exports = function(proxy, allowedHost) { |
||||
return { |
||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
||||
// websites from potentially accessing local content through DNS rebinding:
|
||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
||||
// However, it made several existing use cases such as development in cloud
|
||||
// environment or subdomains in development significantly more complicated:
|
||||
// https://github.com/facebook/create-react-app/issues/2271
|
||||
// https://github.com/facebook/create-react-app/issues/2233
|
||||
// While we're investigating better solutions, for now we will take a
|
||||
// compromise. Since our WDS configuration only serves files in the `public`
|
||||
// folder we won't consider accessing them a vulnerability. However, if you
|
||||
// use the `proxy` feature, it gets more dangerous because it can expose
|
||||
// remote code execution vulnerabilities in backends like Django and Rails.
|
||||
// So we will disable the host check normally, but enable it if you have
|
||||
// specified the `proxy` setting. Finally, we let you override it if you
|
||||
// really know what you're doing with a special environment variable.
|
||||
disableHostCheck: |
||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true', |
||||
// Enable gzip compression of generated files.
|
||||
compress: true, |
||||
// Silence WebpackDevServer's own logs since they're generally not useful.
|
||||
// It will still show compile warnings and errors with this setting.
|
||||
clientLogLevel: 'none', |
||||
// By default WebpackDevServer serves physical files from current directory
|
||||
// in addition to all the virtual build products that it serves from memory.
|
||||
// This is confusing because those files won’t automatically be available in
|
||||
// production build folder unless we copy them. However, copying the whole
|
||||
// project directory is dangerous because we may expose sensitive files.
|
||||
// Instead, we establish a convention that only files in `public` directory
|
||||
// get served. Our build script will copy `public` into the `build` folder.
|
||||
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
||||
// Note that we only recommend to use `public` folder as an escape hatch
|
||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
||||
// for some reason broken when imported through Webpack. If you just want to
|
||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
||||
contentBase: paths.appPublic, |
||||
// By default files from `contentBase` will not trigger a page reload.
|
||||
watchContentBase: true, |
||||
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
|
||||
// for the WebpackDevServer client so it can learn when the files were
|
||||
// updated. The WebpackDevServer client is included as an entry point
|
||||
// in the Webpack development configuration. Note that only changes
|
||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
||||
hot: true, |
||||
// It is important to tell WebpackDevServer to use the same "root" path
|
||||
// as we specified in the config. In development, we always serve from /.
|
||||
publicPath: config.output.publicPath, |
||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
||||
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
|
||||
quiet: true, |
||||
// Reportedly, this avoids CPU overload on some systems.
|
||||
// https://github.com/facebook/create-react-app/issues/293
|
||||
// src/node_modules is not ignored to support absolute imports
|
||||
// https://github.com/facebook/create-react-app/issues/1065
|
||||
watchOptions: { |
||||
ignored: ignoredFiles(paths.appSrc), |
||||
}, |
||||
// Enable HTTPS if the HTTPS environment variable is set to 'true'
|
||||
https: protocol === 'https', |
||||
host, |
||||
overlay: false, |
||||
historyApiFallback: { |
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebook/create-react-app/issues/387.
|
||||
disableDotRule: true, |
||||
}, |
||||
public: allowedHost, |
||||
proxy, |
||||
before(app, server) { |
||||
if (fs.existsSync(paths.proxySetup)) { |
||||
// This registers user provided middleware for proxy reasons
|
||||
require(paths.proxySetup)(app); |
||||
} |
||||
|
||||
// This lets us fetch source contents from webpack for the error overlay
|
||||
app.use(evalSourceMapMiddleware(server)); |
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware()); |
||||
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware()); |
||||
}, |
||||
}; |
||||
}; |
@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
|
||||
{ |
||||
"name": "easydev-seed", |
||||
"version": "2.0.0", |
||||
"private": true, |
||||
"homepage": "http://previews.aspirity.com/easydev", |
||||
"dependencies": { |
||||
"bfj": "^6.1.1", |
||||
"bootstrap": "^4.3.1", |
||||
"case-sensitive-paths-webpack-plugin": "^2.2.0", |
||||
"chalk": "^2.4.2", |
||||
"classnames": "^2.2.6", |
||||
"dotenv": "^6.2.0", |
||||
"dotenv-expand": "^4.2.0", |
||||
"file-loader": "^3.0.1", |
||||
"fs-extra": "^7.0.1", |
||||
"html-webpack-plugin": "4.0.0-alpha.2", |
||||
"identity-obj-proxy": "^3.0.0", |
||||
"jest": "^24.3.1", |
||||
"jest-pnp-resolver": "^1.2.1", |
||||
"jest-resolve": "^24.3.1", |
||||
"mdi-react": "^5.3.0", |
||||
"mini-css-extract-plugin": "^0.5.0", |
||||
"optimize-css-assets-webpack-plugin": "^5.0.1", |
||||
"pnp-webpack-plugin": "^1.4.1", |
||||
"postcss-flexbugs-fixes": "^4.1.0", |
||||
"postcss-loader": "^3.0.0", |
||||
"postcss-preset-env": "^6.6.0", |
||||
"postcss-safe-parser": "^4.0.1", |
||||
"prop-types": "^15.7.2", |
||||
"react": "^16.8.4", |
||||
"react-app-polyfill": "^0.2.2", |
||||
"react-dev-utils": "6.0.3", |
||||
"react-dom": "^16.8.4", |
||||
"react-redux": "^6.0.1", |
||||
"react-router": "^4.3.1", |
||||
"react-router-dom": "^4.3.1", |
||||
"react-smooth-scrollbar": "^8.0.6", |
||||
"reactstrap": "^7.1.0", |
||||
"redux": "^4.0.1", |
||||
"redux-form": "^8.1.0", |
||||
"redux-thunk": "^2.3.0", |
||||
"resolve": "1.10.0", |
||||
"smooth-scrollbar": "^8.3.1", |
||||
"style-loader": "0.23.1", |
||||
"terser-webpack-plugin": "1.2.3", |
||||
"url-loader": "1.1.2" |
||||
}, |
||||
"scripts": { |
||||
"start": "node scripts/start.js", |
||||
"build": "node scripts/build.js", |
||||
"test": "node scripts/test.js" |
||||
}, |
||||
"devDependencies": { |
||||
"@babel/core": "7.3.4", |
||||
"@svgr/webpack": "4.1.0", |
||||
"babel-core": "7.0.0-bridge.0", |
||||
"babel-eslint": "10.0.1", |
||||
"babel-jest": "24.3.1", |
||||
"babel-loader": "8.0.5", |
||||
"babel-plugin-named-asset-import": "^0.3.1", |
||||
"babel-preset-react-app": "^7.0.2", |
||||
"babel-runtime": "6.26.0", |
||||
"css-loader": "2.1.1", |
||||
"eslint": "5.15.1", |
||||
"eslint-config-airbnb": "^17.1.0", |
||||
"eslint-config-react-app": "^3.0.8", |
||||
"eslint-loader": "2.1.2", |
||||
"eslint-plugin-flowtype": "3.4.2", |
||||
"eslint-plugin-import": "2.16.0", |
||||
"eslint-plugin-jsx-a11y": "6.2.1", |
||||
"eslint-plugin-react": "7.12.4", |
||||
"node-sass": "^4.11.0", |
||||
"react-hot-loader": "^4.8.0", |
||||
"sass-loader": "7.1.0", |
||||
"webpack": "4.29.6", |
||||
"webpack-dev-server": "3.2.1", |
||||
"webpack-manifest-plugin": "2.0.4", |
||||
"workbox-webpack-plugin": "4.1.0" |
||||
}, |
||||
"eslintConfig": { |
||||
"extends": "react-app" |
||||
}, |
||||
"browserslist": [ |
||||
">0.2%", |
||||
"not dead", |
||||
"not ie <= 11", |
||||
"not op_mini all" |
||||
], |
||||
"jest": { |
||||
"collectCoverageFrom": [ |
||||
"src/**/*.{js,jsx}" |
||||
], |
||||
"resolver": "jest-pnp-resolver", |
||||
"setupFiles": [ |
||||
"react-app-polyfill/jsdom" |
||||
], |
||||
"testMatch": [ |
||||
"<rootDir>/src/**/__tests__/**/*.{js,jsx}", |
||||
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx}" |
||||
], |
||||
"testEnvironment": "jsdom", |
||||
"testURL": "http://localhost", |
||||
"transform": { |
||||
"^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest", |
||||
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js", |
||||
"^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js" |
||||
}, |
||||
"transformIgnorePatterns": [ |
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$", |
||||
"^.+\\.module\\.(css|sass|scss)$" |
||||
], |
||||
"moduleNameMapper": { |
||||
"^react-native$": "react-native-web", |
||||
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy" |
||||
}, |
||||
"moduleFileExtensions": [ |
||||
"web.js", |
||||
"js", |
||||
"json", |
||||
"web.jsx", |
||||
"jsx", |
||||
"node" |
||||
] |
||||
}, |
||||
"babel": { |
||||
"presets": [ |
||||
"react-app" |
||||
] |
||||
} |
||||
} |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 172 B |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||
<meta name="theme-color" content="#000000"> |
||||
<!-- |
||||
manifest.json provides metadata used when your web app is added to the |
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/ |
||||
--> |
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"> |
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/fav.ico"> |
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"> |
||||
<link rel="stylesheet" href="https://cdn.linearicons.com/free/1.0.0/icon-font.min.css"> |
||||
<link rel="stylesheet" href="%PUBLIC_URL%/load.css"> |
||||
<!-- |
||||
Notice the use of %PUBLIC_URL% in the tags above. |
||||
It will be replaced with the URL of the `public` folder during the build. |
||||
Only files inside the `public` folder can be referenced from the HTML. |
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will |
||||
work correctly both with client-side routing and a non-root public URL. |
||||
Learn how to configure a non-root public URL by running `npm run build`. |
||||
--> |
||||
<title>EasyDEV</title> |
||||
</head> |
||||
<body> |
||||
<noscript> |
||||
You need to enable JavaScript to run this app. |
||||
</noscript> |
||||
<div id="root"> |
||||
<!--Loader--> |
||||
<div class="load"> |
||||
<div class="load__icon-wrap"> |
||||
<svg class="load__icon"> |
||||
<path fill="#4ce1b6" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z"/> |
||||
</svg> |
||||
</div> |
||||
</div> |
||||
<!--Loader--> |
||||
</div> |
||||
<!-- |
||||
This HTML file is a template. |
||||
If you open it directly in the browser, you will see an empty page. |
||||
|
||||
You can add webfonts, meta tags, or analytics to this file. |
||||
The build step will place the bundled scripts into the <body> tag. |
||||
|
||||
To begin the development, run `npm start` or `yarn start`. |
||||
To create a production bundle, use `npm run build` or `yarn build`. |
||||
--> |
||||
</body> |
||||
</html> |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
.load__icon { |
||||
animation: linear load 2s infinite; |
||||
width: 32px; |
||||
height: 32px; |
||||
} |
||||
|
||||
.load__icon-wrap { |
||||
margin: auto; |
||||
} |
||||
|
||||
.load { |
||||
height: calc(100vh - 16px); |
||||
width: 100%; |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
@keyframes load { |
||||
from { |
||||
transform: rotate(0deg) scale(2); |
||||
} |
||||
to { |
||||
transform: rotate(360deg) scale(2); |
||||
} |
||||
} |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
{ |
||||
"short_name": "EasyDEV", |
||||
"name": "EasyDEV", |
||||
"icons": [ |
||||
{ |
||||
"src": "fav.ico", |
||||
"sizes": "64x64 32x32 24x24 16x16", |
||||
"type": "image/x-icon" |
||||
} |
||||
], |
||||
"start_url": ".", |
||||
"display": "standalone", |
||||
"theme_color": "#4ce1b6", |
||||
"background_color": "#ffffff" |
||||
} |
@ -0,0 +1,189 @@
@@ -0,0 +1,189 @@
|
||||
'use strict'; |
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'production'; |
||||
process.env.NODE_ENV = 'production'; |
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => { |
||||
throw err; |
||||
}); |
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env'); |
||||
|
||||
|
||||
const path = require('path'); |
||||
const chalk = require('chalk'); |
||||
const fs = require('fs-extra'); |
||||
const webpack = require('webpack'); |
||||
const bfj = require('bfj'); |
||||
const config = require('../config/webpack.config.prod'); |
||||
const paths = require('../config/paths'); |
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); |
||||
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); |
||||
const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); |
||||
const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); |
||||
const printBuildError = require('react-dev-utils/printBuildError'); |
||||
|
||||
const measureFileSizesBeforeBuild = |
||||
FileSizeReporter.measureFileSizesBeforeBuild; |
||||
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; |
||||
const useYarn = fs.existsSync(paths.yarnLockFile); |
||||
|
||||
// These sizes are pretty large. We'll warn for bundles exceeding them.
|
||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; |
||||
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; |
||||
|
||||
const isInteractive = process.stdout.isTTY; |
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { |
||||
process.exit(1); |
||||
} |
||||
|
||||
// Process CLI arguments
|
||||
const argv = process.argv.slice(2); |
||||
const writeStatsJson = argv.indexOf('--stats') !== -1; |
||||
|
||||
// We require that you explictly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper'); |
||||
checkBrowsers(paths.appPath, isInteractive) |
||||
.then(() => { |
||||
// First, read the current file sizes in build directory.
|
||||
// This lets us display how much they changed later.
|
||||
return measureFileSizesBeforeBuild(paths.appBuild); |
||||
}) |
||||
.then(previousFileSizes => { |
||||
// Remove all content but keep the directory so that
|
||||
// if you're in it, you don't end up in Trash
|
||||
fs.emptyDirSync(paths.appBuild); |
||||
// Merge with the public folder
|
||||
copyPublicFolder(); |
||||
// Start the webpack build
|
||||
return build(previousFileSizes); |
||||
}) |
||||
.then( |
||||
({ stats, previousFileSizes, warnings }) => { |
||||
if (warnings.length) { |
||||
console.log(chalk.yellow('Compiled with warnings.\n')); |
||||
console.log(warnings.join('\n\n')); |
||||
console.log( |
||||
'\nSearch for the ' + |
||||
chalk.underline(chalk.yellow('keywords')) + |
||||
' to learn more about each warning.' |
||||
); |
||||
console.log( |
||||
'To ignore, add ' + |
||||
chalk.cyan('// eslint-disable-next-line') + |
||||
' to the line before.\n' |
||||
); |
||||
} else { |
||||
console.log(chalk.green('Compiled successfully.\n')); |
||||
} |
||||
|
||||
console.log('File sizes after gzip:\n'); |
||||
printFileSizesAfterBuild( |
||||
stats, |
||||
previousFileSizes, |
||||
paths.appBuild, |
||||
WARN_AFTER_BUNDLE_GZIP_SIZE, |
||||
WARN_AFTER_CHUNK_GZIP_SIZE |
||||
); |
||||
console.log(); |
||||
|
||||
const appPackage = require(paths.appPackageJson); |
||||
const publicUrl = paths.publicUrl; |
||||
const publicPath = config.output.publicPath; |
||||
const buildFolder = path.relative(process.cwd(), paths.appBuild); |
||||
printHostingInstructions( |
||||
appPackage, |
||||
publicUrl, |
||||
publicPath, |
||||
buildFolder, |
||||
useYarn |
||||
); |
||||
}, |
||||
err => { |
||||
console.log(chalk.red('Failed to compile.\n')); |
||||
printBuildError(err); |
||||
process.exit(1); |
||||
} |
||||
) |
||||
.catch(err => { |
||||
if (err && err.message) { |
||||
console.log(err.message); |
||||
} |
||||
process.exit(1); |
||||
}); |
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) { |
||||
console.log('Creating an optimized production build...'); |
||||
|
||||
let compiler = webpack(config); |
||||
return new Promise((resolve, reject) => { |
||||
compiler.run((err, stats) => { |
||||
let messages; |
||||
if (err) { |
||||
if (!err.message) { |
||||
return reject(err); |
||||
} |
||||
messages = formatWebpackMessages({ |
||||
errors: [err.message], |
||||
warnings: [], |
||||
}); |
||||
} else { |
||||
messages = formatWebpackMessages( |
||||
stats.toJson({ all: false, warnings: true, errors: true }) |
||||
); |
||||
} |
||||
if (messages.errors.length) { |
||||
// Only keep the first error. Others are often indicative
|
||||
// of the same problem, but confuse the reader with noise.
|
||||
if (messages.errors.length > 1) { |
||||
messages.errors.length = 1; |
||||
} |
||||
return reject(new Error(messages.errors.join('\n\n'))); |
||||
} |
||||
if ( |
||||
process.env.CI && |
||||
(typeof process.env.CI !== 'string' || |
||||
process.env.CI.toLowerCase() !== 'false') && |
||||
messages.warnings.length |
||||
) { |
||||
console.log( |
||||
chalk.yellow( |
||||
'\nTreating warnings as errors because process.env.CI = true.\n' + |
||||
'Most CI servers set it automatically.\n' |
||||
) |
||||
); |
||||
return reject(new Error(messages.warnings.join('\n\n'))); |
||||
} |
||||
|
||||
const resolveArgs = { |
||||
stats, |
||||
previousFileSizes, |
||||
warnings: messages.warnings, |
||||
}; |
||||
if (writeStatsJson) { |
||||
return bfj |
||||
.write(paths.appBuild + '/bundle-stats.json', stats.toJson()) |
||||
.then(() => resolve(resolveArgs)) |
||||
.catch(error => reject(new Error(error))); |
||||
} |
||||
|
||||
return resolve(resolveArgs); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
function copyPublicFolder() { |
||||
fs.copySync(paths.appPublic, paths.appBuild, { |
||||
dereference: true, |
||||
filter: file => file !== paths.appHtml, |
||||
}); |
||||
} |
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
'use strict'; |
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'development'; |
||||
process.env.NODE_ENV = 'development'; |
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => { |
||||
throw err; |
||||
}); |
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env'); |
||||
|
||||
|
||||
const fs = require('fs'); |
||||
const chalk = require('chalk'); |
||||
const webpack = require('webpack'); |
||||
const WebpackDevServer = require('webpack-dev-server'); |
||||
const clearConsole = require('react-dev-utils/clearConsole'); |
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); |
||||
const { |
||||
choosePort, |
||||
createCompiler, |
||||
prepareProxy, |
||||
prepareUrls, |
||||
} = require('react-dev-utils/WebpackDevServerUtils'); |
||||
const openBrowser = require('react-dev-utils/openBrowser'); |
||||
const paths = require('../config/paths'); |
||||
const config = require('../config/webpack.config.dev'); |
||||
const createDevServerConfig = require('../config/webpackDevServer.config'); |
||||
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile); |
||||
const isInteractive = process.stdout.isTTY; |
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { |
||||
process.exit(1); |
||||
} |
||||
|
||||
// Tools like Cloud9 rely on this.
|
||||
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; |
||||
const HOST = process.env.HOST || '0.0.0.0'; |
||||
|
||||
if (process.env.HOST) { |
||||
console.log( |
||||
chalk.cyan( |
||||
`Attempting to bind to HOST environment variable: ${chalk.yellow( |
||||
chalk.bold(process.env.HOST) |
||||
)}` |
||||
) |
||||
); |
||||
console.log( |
||||
`If this was unintentional, check that you haven't mistakenly set it in your shell.` |
||||
); |
||||
console.log( |
||||
`Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}` |
||||
); |
||||
console.log(); |
||||
} |
||||
|
||||
// We require that you explictly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper'); |
||||
checkBrowsers(paths.appPath, isInteractive) |
||||
.then(() => { |
||||
// We attempt to use the default port but if it is busy, we offer the user to
|
||||
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
||||
return choosePort(HOST, DEFAULT_PORT); |
||||
}) |
||||
.then(port => { |
||||
if (port == null) { |
||||
// We have not found a port.
|
||||
return; |
||||
} |
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; |
||||
const appName = require(paths.appPackageJson).name; |
||||
const urls = prepareUrls(protocol, HOST, port); |
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = createCompiler(webpack, config, appName, urls, useYarn); |
||||
// Load proxy config
|
||||
const proxySetting = require(paths.appPackageJson).proxy; |
||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic); |
||||
// Serve webpack assets generated by the compiler over a web server.
|
||||
const serverConfig = createDevServerConfig( |
||||
proxyConfig, |
||||
urls.lanUrlForConfig |
||||
); |
||||
const devServer = new WebpackDevServer(compiler, serverConfig); |
||||
// Launch WebpackDevServer.
|
||||
devServer.listen(port, HOST, err => { |
||||
if (err) { |
||||
return console.log(err); |
||||
} |
||||
if (isInteractive) { |
||||
clearConsole(); |
||||
} |
||||
console.log(chalk.cyan('Starting the development server...\n')); |
||||
openBrowser(urls.localUrlForBrowser); |
||||
}); |
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function(sig) { |
||||
process.on(sig, function() { |
||||
devServer.close(); |
||||
process.exit(); |
||||
}); |
||||
}); |
||||
}) |
||||
.catch(err => { |
||||
if (err && err.message) { |
||||
console.log(err.message); |
||||
} |
||||
process.exit(1); |
||||
}); |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
'use strict'; |
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'test'; |
||||
process.env.NODE_ENV = 'test'; |
||||
process.env.PUBLIC_URL = ''; |
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => { |
||||
throw err; |
||||
}); |
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env'); |
||||
|
||||
|
||||
const jest = require('jest'); |
||||
const execSync = require('child_process').execSync; |
||||
let argv = process.argv.slice(2); |
||||
|
||||
function isInGitRepository() { |
||||
try { |
||||
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); |
||||
return true; |
||||
} catch (e) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
function isInMercurialRepository() { |
||||
try { |
||||
execSync('hg --cwd . root', { stdio: 'ignore' }); |
||||
return true; |
||||
} catch (e) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// Watch unless on CI, in coverage mode, or explicitly running all tests
|
||||
if ( |
||||
!process.env.CI && |
||||
argv.indexOf('--coverage') === -1 && |
||||
argv.indexOf('--watchAll') === -1 |
||||
) { |
||||
// https://github.com/facebook/create-react-app/issues/5210
|
||||
const hasSourceControl = isInGitRepository() || isInMercurialRepository(); |
||||
argv.push(hasSourceControl ? '--watch' : '--watchAll'); |
||||
} |
||||
|
||||
|
||||
jest.run(argv); |
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
import React, { Component, Fragment } from 'react'; |
||||
// eslint-disable-next-line import/no-extraneous-dependencies |
||||
import { hot } from 'react-hot-loader'; |
||||
import { Provider } from 'react-redux'; |
||||
import { BrowserRouter } from 'react-router-dom'; |
||||
import 'bootstrap/dist/css/bootstrap.css'; |
||||
import '../../scss/app.scss'; |
||||
import Router from './Router'; |
||||
import store from './store'; |
||||
import ScrollToTop from './ScrollToTop'; |
||||
|
||||
class App extends Component { |
||||
constructor() { |
||||
super(); |
||||
this.state = { |
||||
loading: true, |
||||
loaded: false, |
||||
}; |
||||
} |
||||
|
||||
componentDidMount() { |
||||
window.addEventListener('load', () => { |
||||
this.setState({ loading: false }); |
||||
setTimeout(() => this.setState({ loaded: true }), 500); |
||||
}); |
||||
} |
||||
|
||||
render() { |
||||
const { loaded, loading } = this.state; |
||||
return ( |
||||
<Provider store={store}> |
||||
<BrowserRouter> |
||||
<ScrollToTop> |
||||
<Fragment> |
||||
{!loaded |
||||
&& ( |
||||
<div className={`load${loading ? '' : ' loaded'}`}> |
||||
<div className="load__icon-wrap"> |
||||
<svg className="load__icon"> |
||||
<path fill="#4ce1b6" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" /> |
||||
</svg> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
<div> |
||||
<Router /> |
||||
</div> |
||||
</Fragment> |
||||
</ScrollToTop> |
||||
</BrowserRouter> |
||||
</Provider> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default hot(module)(App); |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
import React from 'react'; |
||||
import ReactDOM from 'react-dom'; |
||||
import App from './App'; |
||||
|
||||
it('renders without crashing', () => { |
||||
const div = document.createElement('div'); |
||||
ReactDOM.render(<App />, div); |
||||
ReactDOM.unmountComponentAtNode(div); |
||||
}); |
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
import React, { PureComponent } from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import PropTypes from 'prop-types'; |
||||
import { ThemeProps } from '../../shared/prop-types/ReducerProps'; |
||||
|
||||
class MainWrapper extends PureComponent { |
||||
static propTypes = { |
||||
theme: ThemeProps.isRequired, |
||||
children: PropTypes.element.isRequired, |
||||
}; |
||||
|
||||
render() { |
||||
const { theme, children } = this.props; |
||||
|
||||
return ( |
||||
<div className={theme.className}> |
||||
<div className="wrapper"> |
||||
{children} |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default connect(state => ({ |
||||
theme: state.theme, |
||||
}))(MainWrapper); |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
import React from 'react'; |
||||
import { Route, Switch } from 'react-router-dom'; |
||||
import Layout from '../Layout/index'; |
||||
import MainWrapper from './MainWrapper'; |
||||
|
||||
import LogIn from '../LogIn/index'; |
||||
import ExamplePageOne from '../Example/index'; |
||||
import ExamplePageTwo from '../ExampleTwo/index'; |
||||
|
||||
const Pages = () => ( |
||||
<Switch> |
||||
<Route path="/pages/one" component={ExamplePageOne} /> |
||||
<Route path="/pages/two" component={ExamplePageTwo} /> |
||||
</Switch> |
||||
); |
||||
|
||||
const wrappedRoutes = () => ( |
||||
<div> |
||||
<Layout /> |
||||
<div className="container__wrap"> |
||||
<Route path="/pages" component={Pages} /> |
||||
</div> |
||||
</div> |
||||
); |
||||
|
||||
const Router = () => ( |
||||
<MainWrapper> |
||||
<main> |
||||
<Switch> |
||||
<Route exact path="/" component={LogIn} /> |
||||
<Route exact path="/log_in" component={LogIn} /> |
||||
<Route path="/" component={wrappedRoutes} /> |
||||
</Switch> |
||||
</main> |
||||
</MainWrapper> |
||||
); |
||||
|
||||
export default Router; |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
import { PureComponent } from 'react'; |
||||
import { withRouter } from 'react-router-dom'; |
||||
import PropTypes from 'prop-types'; |
||||
|
||||
class ScrollToTop extends PureComponent { |
||||
static propTypes = { |
||||
location: PropTypes.shape({ |
||||
pathname: PropTypes.string, |
||||
}).isRequired, |
||||
children: PropTypes.element.isRequired, |
||||
}; |
||||
|
||||
componentDidUpdate(prevProps) { |
||||
const { location } = this.props; |
||||
if (location.pathname !== prevProps.location.pathname) { |
||||
window.scrollTo(0, 0); |
||||
} |
||||
} |
||||
|
||||
render() { |
||||
const { children } = this.props; |
||||
return children; |
||||
} |
||||
} |
||||
|
||||
export default withRouter(ScrollToTop); |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
import { combineReducers } from 'redux'; |
||||
import { reducer as formReducer } from 'redux-form'; |
||||
import { sidebarReducer, themeReducer } from '../../redux/reducers/index'; |
||||
|
||||
const objReducers = { |
||||
form: formReducer, // mounted under "form",
|
||||
theme: themeReducer, |
||||
sidebar: sidebarReducer, |
||||
}; |
||||
|
||||
export default combineReducers(objReducers); |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
import { createStore, compose, applyMiddleware } from 'redux'; |
||||
import thunk from 'redux-thunk'; |
||||
import reducers from './reducers'; |
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; |
||||
const store = createStore( |
||||
reducers, |
||||
{}, |
||||
composeEnhancers(applyMiddleware(thunk)), |
||||
); |
||||
|
||||
export default store; |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import React from 'react'; |
||||
import { Card, CardBody, Col } from 'reactstrap'; |
||||
|
||||
const ExampleCard = () => ( |
||||
<Col md={12}> |
||||
<Card> |
||||
<CardBody> |
||||
<div className="card__title"> |
||||
<h5 className="bold-text">Example title</h5> |
||||
<h5 className="subhead">Example subhead</h5> |
||||
</div> |
||||
<p>Your content here</p> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
); |
||||
|
||||
export default ExampleCard; |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import React from 'react'; |
||||
import { Col, Container, Row } from 'reactstrap'; |
||||
import ExampleCard from './components/ExampleCard'; |
||||
|
||||
const ExamplePage = () => ( |
||||
<Container className="dashboard"> |
||||
<Row> |
||||
<Col md={12}> |
||||
<h3 className="page-title">Example Page One</h3> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<ExampleCard /> |
||||
</Row> |
||||
</Container> |
||||
); |
||||
|
||||
export default ExamplePage; |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import React from 'react'; |
||||
import { Card, CardBody, Col } from 'reactstrap'; |
||||
|
||||
const ExampleCard = () => ( |
||||
<Col md={12}> |
||||
<Card> |
||||
<CardBody> |
||||
<div className="card__title"> |
||||
<h5 className="bold-text">Example title</h5> |
||||
<h5 className="subhead">Example subhead</h5> |
||||
</div> |
||||
<p>Your content here</p> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
); |
||||
|
||||
export default ExampleCard; |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import React from 'react'; |
||||
import { Col, Container, Row } from 'reactstrap'; |
||||
import ExampleCard from './components/ExampleCard'; |
||||
|
||||
const ExamplePage = () => ( |
||||
<Container className="dashboard"> |
||||
<Row> |
||||
<Col md={12}> |
||||
<h3 className="page-title">Example Page Two</h3> |
||||
</Col> |
||||
</Row> |
||||
<Row> |
||||
<ExampleCard /> |
||||
</Row> |
||||
</Container> |
||||
); |
||||
|
||||
export default ExamplePage; |
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
import React, { Component } from 'react'; |
||||
import { withRouter } from 'react-router-dom'; |
||||
import { connect } from 'react-redux'; |
||||
import classNames from 'classnames'; |
||||
import PropTypes from 'prop-types'; |
||||
import Topbar from './topbar/Topbar'; |
||||
import Sidebar from './sidebar/Sidebar'; |
||||
|
||||
import { changeThemeToDark, changeThemeToLight } from '../../redux/actions/themeActions'; |
||||
import { changeMobileSidebarVisibility, changeSidebarVisibility } from '../../redux/actions/sidebarActions'; |
||||
import { SidebarProps } from '../../shared/prop-types/ReducerProps'; |
||||
|
||||
class Layout extends Component { |
||||
static propTypes = { |
||||
dispatch: PropTypes.func.isRequired, |
||||
sidebar: SidebarProps.isRequired, |
||||
}; |
||||
|
||||
changeSidebarVisibility = () => { |
||||
const { dispatch } = this.props; |
||||
dispatch(changeSidebarVisibility()); |
||||
}; |
||||
|
||||
changeMobileSidebarVisibility = () => { |
||||
const { dispatch } = this.props; |
||||
dispatch(changeMobileSidebarVisibility()); |
||||
}; |
||||
|
||||
changeToDark = () => { |
||||
const { dispatch } = this.props; |
||||
dispatch(changeThemeToDark()); |
||||
}; |
||||
|
||||
changeToLight = () => { |
||||
const { dispatch } = this.props; |
||||
dispatch(changeThemeToLight()); |
||||
}; |
||||
|
||||
render() { |
||||
const { sidebar } = this.props; |
||||
|
||||
const layoutClass = classNames({ |
||||
layout: true, |
||||
'layout--collapse': sidebar.collapse, |
||||
}); |
||||
|
||||
return ( |
||||
<div className={layoutClass}> |
||||
<Topbar |
||||
changeMobileSidebarVisibility={this.changeMobileSidebarVisibility} |
||||
changeSidebarVisibility={this.changeSidebarVisibility} |
||||
/> |
||||
<Sidebar |
||||
sidebar={sidebar} |
||||
changeToDark={this.changeToDark} |
||||
changeToLight={this.changeToLight} |
||||
changeMobileSidebarVisibility={this.changeMobileSidebarVisibility} |
||||
/> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default withRouter(connect(state => ({ |
||||
sidebar: state.sidebar, |
||||
}))(Layout)); |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
import React from 'react'; |
||||
import Scrollbar from 'react-smooth-scrollbar'; |
||||
import classNames from 'classnames'; |
||||
import PropTypes from 'prop-types'; |
||||
import SidebarContent from './SidebarContent'; |
||||
import { SidebarProps } from '../../../shared/prop-types/ReducerProps'; |
||||
|
||||
const Sidebar = ({ |
||||
changeToDark, changeToLight, changeMobileSidebarVisibility, sidebar, |
||||
}) => { |
||||
const sidebarClass = classNames({ |
||||
sidebar: true, |
||||
'sidebar--show': sidebar.show, |
||||
'sidebar--collapse': sidebar.collapse, |
||||
}); |
||||
|
||||
return ( |
||||
<div className={sidebarClass}> |
||||
<button type="button" className="sidebar__back" onClick={changeMobileSidebarVisibility} /> |
||||
<Scrollbar className="sidebar__scroll scroll"> |
||||
<div className="sidebar__wrapper sidebar__wrapper--desktop"> |
||||
<SidebarContent |
||||
onClick={() => {}} |
||||
changeToDark={changeToDark} |
||||
changeToLight={changeToLight} |
||||
/> |
||||
</div> |
||||
<div className="sidebar__wrapper sidebar__wrapper--mobile"> |
||||
<SidebarContent |
||||
onClick={changeMobileSidebarVisibility} |
||||
changeToDark={changeToDark} |
||||
changeToLight={changeToLight} |
||||
/> |
||||
</div> |
||||
</Scrollbar> |
||||
</div> |
||||
); |
||||
}; |
||||
|
||||
Sidebar.propTypes = { |
||||
sidebar: SidebarProps.isRequired, |
||||
changeToDark: PropTypes.func.isRequired, |
||||
changeToLight: PropTypes.func.isRequired, |
||||
changeMobileSidebarVisibility: PropTypes.func.isRequired, |
||||
}; |
||||
|
||||
export default Sidebar; |
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
import React, { Component } from 'react'; |
||||
import { Collapse } from 'reactstrap'; |
||||
import PropTypes from 'prop-types'; |
||||
import classNames from 'classnames'; |
||||
|
||||
export default class SidebarCategory extends Component { |
||||
static propTypes = { |
||||
title: PropTypes.string.isRequired, |
||||
icon: PropTypes.string, |
||||
isNew: PropTypes.bool, |
||||
children: PropTypes.arrayOf(PropTypes.element).isRequired, |
||||
}; |
||||
|
||||
static defaultProps = { |
||||
icon: '', |
||||
isNew: false, |
||||
}; |
||||
|
||||
constructor() { |
||||
super(); |
||||
this.state = { |
||||
collapse: false, |
||||
}; |
||||
} |
||||
|
||||
toggle = () => { |
||||
this.setState(prevState => ({ collapse: !prevState.collapse })); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
title, icon, isNew, children, |
||||
} = this.props; |
||||
const { collapse } = this.state; |
||||
const categoryClass = classNames({ |
||||
'sidebar__category-wrap': true, |
||||
'sidebar__category-wrap--open': collapse, |
||||
}); |
||||
|
||||
return ( |
||||
<div className={categoryClass}> |
||||
<button type="button" className="sidebar__link sidebar__category" onClick={this.toggle}> |
||||
{icon ? <span className={`sidebar__link-icon lnr lnr-${icon}`} /> : ''} |
||||
<p className="sidebar__link-title">{title} |
||||
{isNew && <span className="sidebar__category-new" />} |
||||
</p> |
||||
<span className="sidebar__category-icon lnr lnr-chevron-right" /> |
||||
</button> |
||||
<Collapse isOpen={collapse} className="sidebar__submenu-wrap"> |
||||
<ul className="sidebar__submenu"> |
||||
<div> |
||||
{children} |
||||
</div> |
||||
</ul> |
||||
</Collapse> |
||||
</div> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
import React, { Component } from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
import SidebarLink from './SidebarLink'; |
||||
import SidebarCategory from './SidebarCategory'; |
||||
|
||||
class SidebarContent extends Component { |
||||
static propTypes = { |
||||
changeToDark: PropTypes.func.isRequired, |
||||
changeToLight: PropTypes.func.isRequired, |
||||
onClick: PropTypes.func.isRequired, |
||||
}; |
||||
|
||||
hideSidebar = () => { |
||||
const { onClick } = this.props; |
||||
onClick(); |
||||
}; |
||||
|
||||
render() { |
||||
const { changeToDark, changeToLight } = this.props; |
||||
return ( |
||||
<div className="sidebar__content"> |
||||
<ul className="sidebar__block"> |
||||
<SidebarLink title="Log In" icon="exit" route="/log_in" onClick={this.hideSidebar} /> |
||||
<SidebarCategory title="Layout" icon="layers"> |
||||
<button type="button" className="sidebar__link" onClick={changeToLight}> |
||||
<p className="sidebar__link-title">Light Theme</p> |
||||
</button> |
||||
<button type="button" className="sidebar__link" onClick={changeToDark}> |
||||
<p className="sidebar__link-title">Dark Theme</p> |
||||
</button> |
||||
</SidebarCategory> |
||||
</ul> |
||||
<ul className="sidebar__block"> |
||||
<SidebarCategory title="Example Pages" icon="diamond"> |
||||
<SidebarLink title="Page one" route="/pages/one" onClick={this.hideSidebar} /> |
||||
<SidebarLink title="Page two" route="/pages/two" onClick={this.hideSidebar} /> |
||||
</SidebarCategory> |
||||
</ul> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default SidebarContent; |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
import React from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
import { Badge } from 'reactstrap'; |
||||
import { NavLink } from 'react-router-dom'; |
||||
|
||||
const SidebarLink = ({ |
||||
title, icon, newLink, route, onClick, |
||||
}) => ( |
||||
<NavLink |
||||
to={route} |
||||
onClick={onClick} |
||||
activeClassName="sidebar__link-active" |
||||
> |
||||
<li className="sidebar__link"> |
||||
{icon ? <span className={`sidebar__link-icon lnr lnr-${icon}`} /> : ''} |
||||
<p className="sidebar__link-title"> |
||||
{title} |
||||
{newLink ? <Badge className="sidebar__link-badge"><span>New</span></Badge> : ''} |
||||
</p> |
||||
</li> |
||||
</NavLink> |
||||
); |
||||
|
||||
SidebarLink.propTypes = { |
||||
title: PropTypes.string.isRequired, |
||||
icon: PropTypes.string, |
||||
newLink: PropTypes.bool, |
||||
route: PropTypes.string, |
||||
onClick: PropTypes.func, |
||||
}; |
||||
|
||||
SidebarLink.defaultProps = { |
||||
icon: '', |
||||
newLink: false, |
||||
route: '/', |
||||
onClick: () => {}, |
||||
}; |
||||
|
||||
export default SidebarLink; |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
import React, { PureComponent } from 'react'; |
||||
import { Link } from 'react-router-dom'; |
||||
import PropTypes from 'prop-types'; |
||||
import TopbarSidebarButton from './TopbarSidebarButton'; |
||||
import TopbarProfile from './TopbarProfile'; |
||||
|
||||
class Topbar extends PureComponent { |
||||
static propTypes = { |
||||
changeMobileSidebarVisibility: PropTypes.func.isRequired, |
||||
changeSidebarVisibility: PropTypes.func.isRequired, |
||||
}; |
||||
|
||||
render() { |
||||
const { changeMobileSidebarVisibility, changeSidebarVisibility } = this.props; |
||||
|
||||
return ( |
||||
<div className="topbar"> |
||||
<div className="topbar__wrapper"> |
||||
<div className="topbar__left"> |
||||
<TopbarSidebarButton |
||||
changeMobileSidebarVisibility={changeMobileSidebarVisibility} |
||||
changeSidebarVisibility={changeSidebarVisibility} |
||||
/> |
||||
<Link className="topbar__logo" to="/dashboard_default" /> |
||||
</div> |
||||
<div className="topbar__right"> |
||||
<TopbarProfile /> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default Topbar; |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
import React, { PureComponent } from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
import { Link } from 'react-router-dom'; |
||||
|
||||
export default class TopbarMenuLinks extends PureComponent { |
||||
static propTypes = { |
||||
title: PropTypes.string.isRequired, |
||||
icon: PropTypes.string.isRequired, |
||||
path: PropTypes.string.isRequired, |
||||
}; |
||||
|
||||
render() { |
||||
const { title, icon, path } = this.props; |
||||
|
||||
return ( |
||||
<Link className="topbar__link" to={path}> |
||||
<span className={`topbar__link-icon lnr lnr-${icon}`} /> |
||||
<p className="topbar__link-title">{title}</p> |
||||
</Link> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
import React, { PureComponent } from 'react'; |
||||
import DownIcon from 'mdi-react/ChevronDownIcon'; |
||||
import { Collapse } from 'reactstrap'; |
||||
import TopbarMenuLink from './TopbarMenuLink'; |
||||
|
||||
const Ava = `${process.env.PUBLIC_URL}/img/ava.png`; |
||||
|
||||
export default class TopbarProfile extends PureComponent { |
||||
constructor() { |
||||
super(); |
||||
this.state = { |
||||
collapse: false, |
||||
}; |
||||
} |
||||
|
||||
toggle = () => { |
||||
this.setState(prevState => ({ collapse: !prevState.collapse })); |
||||
}; |
||||
|
||||
render() { |
||||
const { collapse } = this.state; |
||||
|
||||
return ( |
||||
<div className="topbar__profile"> |
||||
<button type="button" className="topbar__avatar" onClick={this.toggle}> |
||||
<img className="topbar__avatar-img" src={Ava} alt="avatar" /> |
||||
<p className="topbar__avatar-name">Roman Johanson</p> |
||||
<DownIcon className="topbar__icon" /> |
||||
</button> |
||||
{collapse && <button type="button" className="topbar__back" onClick={this.toggle} />} |
||||
<Collapse isOpen={collapse} className="topbar__menu-wrap"> |
||||
<div className="topbar__menu"> |
||||
<TopbarMenuLink title="Page one" icon="list" path="/pages/one" /> |
||||
<TopbarMenuLink title="Page two" icon="inbox" path="/pages/two" /> |
||||
<div className="topbar__menu-divider" /> |
||||
<TopbarMenuLink title="Log Out" icon="exit" path="/" /> |
||||
</div> |
||||
</Collapse> |
||||
</div> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
import React, { PureComponent } from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
|
||||
const icon = `${process.env.PUBLIC_URL}/img/burger.svg`; |
||||
|
||||
class TopbarSidebarButton extends PureComponent { |
||||
static propTypes = { |
||||
changeMobileSidebarVisibility: PropTypes.func.isRequired, |
||||
changeSidebarVisibility: PropTypes.func.isRequired, |
||||
}; |
||||
|
||||
render() { |
||||
const { changeMobileSidebarVisibility, changeSidebarVisibility } = this.props; |
||||
|
||||
return ( |
||||
<div> |
||||
<button type="button" className="topbar__button topbar__button--desktop" onClick={changeSidebarVisibility}> |
||||
<img src={icon} alt="" className="topbar__button-icon" /> |
||||
</button> |
||||
<button type="button" className="topbar__button topbar__button--mobile" onClick={changeMobileSidebarVisibility}> |
||||
<img src={icon} alt="" className="topbar__button-icon" /> |
||||
</button> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default TopbarSidebarButton; |
@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
|
||||
import React, { PureComponent } from 'react'; |
||||
import { Field, reduxForm } from 'redux-form'; |
||||
import EyeIcon from 'mdi-react/EyeIcon'; |
||||
import KeyVariantIcon from 'mdi-react/KeyVariantIcon'; |
||||
import AccountOutlineIcon from 'mdi-react/AccountOutlineIcon'; |
||||
import { Link } from 'react-router-dom'; |
||||
import PropTypes from 'prop-types'; |
||||
import renderCheckBoxField from '../../../shared/components/form/CheckBox'; |
||||
|
||||
class LogInForm extends PureComponent { |
||||
static propTypes = { |
||||
handleSubmit: PropTypes.func.isRequired, |
||||
}; |
||||
|
||||
constructor() { |
||||
super(); |
||||
this.state = { |
||||
showPassword: false, |
||||
}; |
||||
} |
||||
|
||||
showPassword = (e) => { |
||||
e.preventDefault(); |
||||
this.setState(prevState => ({ showPassword: !prevState.showPassword })); |
||||
}; |
||||
|
||||
render() { |
||||
const { handleSubmit } = this.props; |
||||
const { showPassword } = this.state; |
||||
|
||||
return ( |
||||
<form className="form" onSubmit={handleSubmit}> |
||||
<div className="form__form-group"> |
||||
<span className="form__form-group-label">Username</span> |
||||
<div className="form__form-group-field"> |
||||
<div className="form__form-group-icon"> |
||||
<AccountOutlineIcon /> |
||||
</div> |
||||
<Field |
||||
name="name" |
||||
component="input" |
||||
type="text" |
||||
placeholder="Name" |
||||
/> |
||||
</div> |
||||
</div> |
||||
<div className="form__form-group"> |
||||
<span className="form__form-group-label">Password</span> |
||||
<div className="form__form-group-field"> |
||||
<div className="form__form-group-icon"> |
||||
<KeyVariantIcon /> |
||||
</div> |
||||
<Field |
||||
name="password" |
||||
component="input" |
||||
type={showPassword ? 'text' : 'password'} |
||||
placeholder="Password" |
||||
/> |
||||
<button |
||||
className={`form__form-group-button${showPassword ? ' active' : ''}`} |
||||
onClick={e => this.showPassword(e)} |
||||
type="button" |
||||
><EyeIcon /> |
||||
</button> |
||||
</div> |
||||
<div className="account__forgot-password"> |
||||
<a href="/">Forgot a password?</a> |
||||
</div> |
||||
</div> |
||||
<div className="form__form-group"> |
||||
<div className="form__form-group-field"> |
||||
<Field |
||||
name="remember_me" |
||||
component={renderCheckBoxField} |
||||
label="Remember me" |
||||
/> |
||||
</div> |
||||
</div> |
||||
<Link className="btn btn-primary account__btn account__btn--small" to="/pages/one">Sign In</Link> |
||||
<Link className="btn btn-outline-primary account__btn account__btn--small" to="/log_in">Create Account</Link> |
||||
</form> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default reduxForm({ |
||||
form: 'log_in_form', |
||||
})(LogInForm); |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
import React from 'react'; |
||||
import { Link } from 'react-router-dom'; |
||||
import FacebookIcon from 'mdi-react/FacebookIcon'; |
||||
import GooglePlusIcon from 'mdi-react/GooglePlusIcon'; |
||||
import LogInForm from './components/LogInForm'; |
||||
|
||||
const LogIn = () => ( |
||||
<div className="account"> |
||||
<div className="account__wrapper"> |
||||
<div className="account__card"> |
||||
<div className="account__head"> |
||||
<h3 className="account__title">Welcome to |
||||
<span className="account__logo"> Easy |
||||
<span className="account__logo-accent">DEV</span> |
||||
</span> |
||||
</h3> |
||||
<h4 className="account__subhead subhead">Start your business easily</h4> |
||||
</div> |
||||
<LogInForm onSubmit /> |
||||
<div className="account__or"> |
||||
<p>Or Easily Using</p> |
||||
</div> |
||||
<div className="account__social"> |
||||
<Link |
||||
className="account__social-btn account__social-btn--facebook" |
||||
to="/pages/one" |
||||
><FacebookIcon /> |
||||
</Link> |
||||
<Link |
||||
className="account__social-btn account__social-btn--google" |
||||
to="/pages/one" |
||||
><GooglePlusIcon /> |
||||
</Link> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
|
||||
export default LogIn; |
||||
|
||||
// if you want to add select, date-picker and time-picker in your app you need to uncomment the first |
||||
// four lines in /scss/components/form.scss to add styles |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
import React from 'react'; |
||||
import { render } from 'react-dom'; |
||||
import App from './containers/App/App'; |
||||
|
||||
render( |
||||
<App />, |
||||
document.getElementById('root'), |
||||
); |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
export const CHANGE_SIDEBAR_VISIBILITY = 'CHANGE_SIDEBAR_VISIBILITY'; |
||||
export const CHANGE_MOBILE_SIDEBAR_VISIBILITY = 'CHANGE_MOBILE_SIDEBAR_VISIBILITY'; |
||||
|
||||
export function changeSidebarVisibility() { |
||||
return { |
||||
type: CHANGE_SIDEBAR_VISIBILITY, |
||||
}; |
||||
} |
||||
|
||||
export function changeMobileSidebarVisibility() { |
||||
return { |
||||
type: CHANGE_MOBILE_SIDEBAR_VISIBILITY, |
||||
}; |
||||
} |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
export const CHANGE_THEME_TO_DARK = 'CHANGE_THEME_TO_DARK'; |
||||
export const CHANGE_THEME_TO_LIGHT = 'CHANGE_THEME_TO_LIGHT'; |
||||
|
||||
export function changeThemeToDark() { |
||||
return { |
||||
type: CHANGE_THEME_TO_DARK, |
||||
}; |
||||
} |
||||
|
||||
export function changeThemeToLight() { |
||||
return { |
||||
type: CHANGE_THEME_TO_LIGHT, |
||||
}; |
||||
} |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
import themeReducer from './themeReducer'; |
||||
import sidebarReducer from './sidebarReducer'; |
||||
|
||||
export { |
||||
themeReducer, |
||||
sidebarReducer, |
||||
}; |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
import { |
||||
CHANGE_SIDEBAR_VISIBILITY, |
||||
CHANGE_MOBILE_SIDEBAR_VISIBILITY, |
||||
} from '../actions/sidebarActions'; |
||||
|
||||
const initialState = { |
||||
show: false, |
||||
collapse: false, |
||||
}; |
||||
|
||||
export default function (state = initialState, action) { |
||||
switch (action.type) { |
||||
case CHANGE_SIDEBAR_VISIBILITY: |
||||
return { ...state, collapse: !state.collapse }; |
||||
case CHANGE_MOBILE_SIDEBAR_VISIBILITY: |
||||
return { ...state, show: !state.show }; |
||||
default: |
||||
return state; |
||||
} |
||||
} |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
import { |
||||
CHANGE_THEME_TO_DARK, |
||||
CHANGE_THEME_TO_LIGHT, |
||||
} from '../actions/themeActions'; |
||||
|
||||
const initialState = { |
||||
className: 'theme-light', |
||||
}; |
||||
|
||||
export default function (state = initialState, action) { |
||||
switch (action.type) { |
||||
case CHANGE_THEME_TO_DARK: |
||||
return { className: 'theme-dark' }; |
||||
case CHANGE_THEME_TO_LIGHT: |
||||
return { className: 'theme-light' }; |
||||
default: |
||||
return state; |
||||
} |
||||
} |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
@import 'settings/variable'; //themes' colors |
||||
|
||||
//base... |
||||
@import 'generic/normalize.scss'; |
||||
@import 'generic/box-sizing.scss'; |
||||
@import './base/scaffolding'; // styles of base elements |
||||
@import './base/typography'; // base styles of h1-h6, p, span |
||||
|
||||
//components... |
||||
@import 'component/btn'; |
||||
@import './component/card'; |
||||
@import './component/check-box'; |
||||
@import './component/sidebar'; |
||||
@import './component/topbar'; |
||||
@import './component/load'; |
||||
@import './component/form'; |
||||
|
||||
//pages... |
||||
@import 'containers/account'; |
||||
|
||||
//objects... |
||||
@import 'objects/layout.scss'; |
||||
|
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
main { |
||||
padding: 0; |
||||
} |
||||
|
||||
body { |
||||
margin: 0; |
||||
padding: 0; |
||||
box-sizing: border-box; |
||||
font-family: 'Roboto', sans-serif; |
||||
font-size: 13px; |
||||
line-height: 1.6; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
} |
||||
|
||||
* { |
||||
box-sizing: inherit; |
||||
} |
||||
|
||||
ul, ol { |
||||
padding-left: 15px; |
||||
margin-bottom: 0; |
||||
} |
||||
|
||||
a { |
||||
color: $color-blue; |
||||
transition: all 0.3s; |
||||
|
||||
&:hover { |
||||
text-decoration: none; |
||||
color: $color-blue-hover; |
||||
} |
||||
} |
||||
|
||||
img { |
||||
width: 100%; |
||||
} |
@ -0,0 +1,150 @@
@@ -0,0 +1,150 @@
|
||||
p, h1, h2, h3, h4, h5, h6 { |
||||
margin-top: 0; |
||||
font-weight: 400; |
||||
margin-bottom: 0; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
} |
||||
|
||||
p { |
||||
margin: 10px 0 0 0; |
||||
|
||||
&:first-child { |
||||
margin: 0; |
||||
} |
||||
} |
||||
|
||||
h1 { |
||||
font-size: 36px; |
||||
line-height: 48px; |
||||
|
||||
&.subhead { |
||||
font-size: 30px; |
||||
color: $color-additional; |
||||
line-height: 36px; |
||||
} |
||||
} |
||||
|
||||
h2 { |
||||
font-size: 30px; |
||||
line-height: 36px; |
||||
|
||||
&.subhead { |
||||
font-size: 24px; |
||||
color: $color-additional; |
||||
line-height: 32px; |
||||
} |
||||
} |
||||
|
||||
h3 { |
||||
font-size: 24px; |
||||
line-height: 32px; |
||||
|
||||
&.subhead { |
||||
font-size: 18px; |
||||
color: $color-additional; |
||||
line-height: 24px; |
||||
} |
||||
} |
||||
|
||||
h4 { |
||||
font-size: 18px; |
||||
line-height: 24px; |
||||
|
||||
&.subhead { |
||||
font-size: 12px; |
||||
color: $color-additional; |
||||
line-height: 16px; |
||||
} |
||||
} |
||||
|
||||
h5 { |
||||
font-size: 14px; |
||||
line-height: 18px; |
||||
|
||||
&.subhead { |
||||
font-size: 10px; |
||||
color: $color-additional; |
||||
line-height: 12px; |
||||
} |
||||
} |
||||
|
||||
h6 { |
||||
font-size: 12px; |
||||
line-height: 16px; |
||||
|
||||
&.subhead { |
||||
font-size: 8px; |
||||
color: $color-additional; |
||||
line-height: 10px; |
||||
} |
||||
} |
||||
|
||||
.bold-text { |
||||
font-weight: 700; |
||||
} |
||||
|
||||
.typography--inline { |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
|
||||
* { |
||||
margin-top: auto; |
||||
margin-right: 15px; |
||||
} |
||||
} |
||||
|
||||
.typography-card { |
||||
|
||||
h1, h2, h3, h4, h5, h6 { |
||||
margin-bottom: 10px; |
||||
} |
||||
} |
||||
|
||||
blockquote { |
||||
font-size: 13px; |
||||
font-style: italic; |
||||
border-left: 3px solid $color-accent; |
||||
margin-bottom: 0; |
||||
padding-left: 10px; |
||||
} |
||||
|
||||
.highlight { |
||||
background-color: $color-accent; |
||||
color: #ffffff; |
||||
} |
||||
|
||||
.red-text { |
||||
color: #ad4444; |
||||
} |
||||
|
||||
.page-title { |
||||
font-weight: 500; |
||||
text-transform: capitalize; |
||||
font-size: 20px; |
||||
|
||||
&:last-child { |
||||
margin-bottom: 30px; |
||||
} |
||||
|
||||
&.page-title--not-last { |
||||
margin-top: 20px; |
||||
} |
||||
} |
||||
|
||||
.page-subhead { |
||||
margin-bottom: 20px; |
||||
|
||||
&.subhead { |
||||
font-size: 14px; |
||||
opacity: 0.7; |
||||
} |
||||
} |
||||
|
||||
::selection { |
||||
color: #ffffff; |
||||
background: $color-accent; |
||||
} |
||||
|
@ -0,0 +1,433 @@
@@ -0,0 +1,433 @@
|
||||
.btn { |
||||
border-radius: 5px; |
||||
padding: 10px 25px; |
||||
margin-bottom: 20px; |
||||
margin-right: 15px; |
||||
transition: all 0.4s; |
||||
font-size: 14px; |
||||
position: relative; |
||||
overflow: hidden; |
||||
z-index: 0; |
||||
|
||||
&:last-child { |
||||
margin-right: 0; |
||||
} |
||||
|
||||
&:before { |
||||
position: absolute; |
||||
height: 0; |
||||
width: 0; |
||||
border-radius: 50%; |
||||
background-color: $color-accent; |
||||
transition: width 0.3s ease-in-out, height 0.3s ease-in-out; |
||||
transform: translate(-50%, -50%); |
||||
z-index: -1; |
||||
content: ""; |
||||
top: 0; |
||||
left: 0; |
||||
} |
||||
|
||||
p { |
||||
display: flex; |
||||
transition: all 0.3s; |
||||
font-weight: 500; |
||||
color: #444444; |
||||
} |
||||
|
||||
svg { |
||||
height: 14px; |
||||
width: 14px; |
||||
margin: 2px 5px 0 0; |
||||
transition: all 0.3s; |
||||
fill: #444444; |
||||
} |
||||
|
||||
&:hover, &:focus, &:active, &:active:focus { |
||||
outline: none; |
||||
box-shadow: none !important; |
||||
|
||||
&:before { |
||||
height: 500%; |
||||
width: 225%; |
||||
} |
||||
} |
||||
|
||||
&:focus, &:active, &:active:focus { |
||||
|
||||
&:before { |
||||
transition: all 0s; |
||||
} |
||||
} |
||||
|
||||
&.square { |
||||
border-radius: 0; |
||||
} |
||||
|
||||
&.rounded { |
||||
border-radius: 30px !important; |
||||
} |
||||
|
||||
&.icon { |
||||
padding: 10px 15px; |
||||
|
||||
&:hover { |
||||
|
||||
p { |
||||
color: #646777; |
||||
} |
||||
|
||||
svg { |
||||
fill: #646777; |
||||
} |
||||
} |
||||
|
||||
&.icon--right { |
||||
|
||||
svg { |
||||
margin: 2px 0 0 5px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
&.btn-sm { |
||||
padding: 5px 25px; |
||||
font-size: 14px; |
||||
} |
||||
|
||||
&.btn-lg { |
||||
padding: 12px 25px; |
||||
font-size: 14px; |
||||
} |
||||
|
||||
&.btn-secondary { |
||||
background-color: #e7e2e2; |
||||
border-color: #e7e2e2; |
||||
color: #646777; |
||||
|
||||
&:before { |
||||
background-color: darken(#e7e2e2, 10%); |
||||
} |
||||
|
||||
&:hover, &:focus, &:active, &:active:focus { |
||||
border-color: darken(#e7e2e2, 10%); |
||||
color: #646777; |
||||
} |
||||
|
||||
&:not([disabled]):not(.disabled):active, &:not([disabled]):not(.disabled).active { |
||||
background-color: #dddddd; |
||||
border-color: #dddddd; |
||||
} |
||||
} |
||||
|
||||
&.btn-outline-secondary { |
||||
border-color: #e7e2e2; |
||||
|
||||
&, p { |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
} |
||||
|
||||
svg { |
||||
|
||||
@include themify($themes) { |
||||
fill: themed('colorText'); |
||||
} |
||||
} |
||||
|
||||
&:before { |
||||
background-color: #e7e2e2; |
||||
} |
||||
|
||||
&:hover, &:focus, &:active, &:active:focus { |
||||
background: transparent; |
||||
border-color: #e7e2e2; |
||||
color: #444444; |
||||
|
||||
p { |
||||
color: #444444; |
||||
} |
||||
|
||||
svg { |
||||
fill: #444444; |
||||
} |
||||
} |
||||
} |
||||
|
||||
&.btn-primary { |
||||
background-color: $color-blue; |
||||
border-color: $color-blue; |
||||
|
||||
&:before { |
||||
background-color: $color-blue-hover; |
||||
} |
||||
|
||||
&:hover, &:focus, &:active, &:active:focus { |
||||
border-color: $color-blue-hover; |
||||
} |
||||
} |
||||
|
||||
&.btn-outline-primary { |
||||
color: $color-blue; |
||||
border-color: $color-blue; |
||||
|
||||
p { |
||||
color: $color-blue; |
||||
} |
||||
|
||||
svg { |
||||
fill: $color-blue; |
||||
} |
||||
|
||||
&:before { |
||||
background-color: $color-blue; |
||||
} |
||||
} |
||||
|
||||
&.btn-success { |
||||
background-color: $color-accent; |
||||
border-color: $color-accent; |
||||
|
||||
&:before { |
||||
background-color: $color-accent-hover; |
||||
} |
||||
|
||||
&:hover, &:focus, &:active, &:active:focus { |
||||
border-color: $color-accent-hover; |
||||
} |
||||
} |
||||
|
||||
&.btn-outline-success { |
||||
color: $color-accent; |
||||
border-color: $color-accent; |
||||
|
||||
p { |
||||
color: $color-accent; |
||||
} |
||||
|
||||
svg { |
||||
fill: $color-accent; |
||||
} |
||||
|
||||
&:before { |
||||
background-color: $color-accent; |
||||
} |
||||
} |
||||
|
||||
&.btn-warning { |
||||
background-color: $color-yellow; |
||||
border-color: $color-yellow; |
||||
color: #ffffff; |
||||
|
||||
&:before { |
||||
background-color: $color-yellow-hover; |
||||
} |
||||
|
||||
&:hover, &:focus, &:active, &:active:focus { |
||||
border-color: $color-yellow-hover; |
||||
} |
||||
} |
||||
|
||||
&.btn-outline-warning { |
||||
color: $color-yellow; |
||||
border-color: $color-yellow; |
||||
|
||||
p { |
||||
color: $color-yellow; |
||||
} |
||||
|
||||
svg { |
||||
fill: $color-yellow; |
||||
} |
||||
|
||||
&:before { |
||||
background-color: $color-yellow; |
||||
} |
||||
} |
||||
|
||||
&.btn-danger { |
||||
background-color: $color-red; |
||||
border-color: $color-red; |
||||
|
||||
&:before { |
||||
background-color: $color-red-hover; |
||||
} |
||||
|
||||
&:hover, &:focus, &:active, &:active:focus { |
||||
border-color: $color-red-hover; |
||||
} |
||||
} |
||||
|
||||
&.btn-outline-danger { |
||||
color: $color-red; |
||||
border-color: $color-red; |
||||
|
||||
p { |
||||
color: $color-red; |
||||
} |
||||
|
||||
svg { |
||||
fill: $color-red; |
||||
} |
||||
|
||||
&:before { |
||||
background-color: $color-red; |
||||
} |
||||
} |
||||
|
||||
&, &.btn-primary, &.btn-danger, &.btn-warning, &.btn-success, &.btn-outline-secondary, &.btn-secondary, |
||||
&.btn-outline-primary, &.btn-outline-danger, &.btn-outline-warning, &.btn-outline-success, |
||||
&.icon, &.icon.btn-secondary { |
||||
|
||||
&.disabled { |
||||
background-color: #f2f4f7; |
||||
border-color: #f2f4f7; |
||||
color: #dddddd; |
||||
pointer-events: none; |
||||
|
||||
p { |
||||
color: #dddddd; |
||||
} |
||||
|
||||
svg { |
||||
fill: #dddddd; |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
&.btn-primary, &.btn-danger, &.btn-warning, &.btn-success { |
||||
p { |
||||
color: #ffffff; |
||||
} |
||||
|
||||
svg { |
||||
fill: #ffffff; |
||||
} |
||||
} |
||||
|
||||
&.btn-outline-primary, &.btn-outline-danger, &.btn-outline-warning, &.btn-outline-success { |
||||
|
||||
&:hover, &:focus, &:active, &:active:focus { |
||||
color: #ffffff; |
||||
background: transparent; |
||||
} |
||||
} |
||||
|
||||
&.btn-primary, &.btn-danger, &.btn-warning, &.btn-success, |
||||
&.btn-outline-primary, &.btn-outline-danger, &.btn-outline-warning, &.btn-outline-success { |
||||
|
||||
&:hover, &:focus, |
||||
&:active, &:active:focus { |
||||
|
||||
p { |
||||
color: #ffffff; |
||||
} |
||||
|
||||
svg { |
||||
fill: #ffffff; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
&.expand { |
||||
|
||||
svg { |
||||
width: 0; |
||||
transition: all 0.3s; |
||||
} |
||||
|
||||
&.expand--load { |
||||
|
||||
@keyframes rotating { |
||||
from { |
||||
transform: rotate(0deg); |
||||
} |
||||
to { |
||||
transform: rotate(360deg); |
||||
} |
||||
} |
||||
svg { |
||||
width: 14px; |
||||
animation: rotating 2s linear infinite; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.btn-toolbar { |
||||
margin-top: 15px; |
||||
margin-bottom: 10px; |
||||
|
||||
& > * { |
||||
margin-right: 15px; |
||||
|
||||
&:last-child { |
||||
margin-right: 0; |
||||
} |
||||
} |
||||
|
||||
&:first-child { |
||||
margin-top: 0; |
||||
} |
||||
|
||||
&:last-child { |
||||
margin-bottom: -10px; |
||||
} |
||||
|
||||
&.btn-toolbar--center { |
||||
|
||||
& > * { |
||||
margin-right: auto; |
||||
margin-left: auto; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.btn-group { |
||||
border-radius: 5px; |
||||
margin-bottom: -10px; |
||||
|
||||
.btn { |
||||
margin-right: 0; |
||||
padding: 10px 15px; |
||||
font-weight: 500; |
||||
} |
||||
|
||||
&.btn-group--justified { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
width: 100%; |
||||
|
||||
.btn { |
||||
width: 100%; |
||||
} |
||||
} |
||||
|
||||
&.btn-group--icons { |
||||
|
||||
.btn { |
||||
padding: 7px 8px; |
||||
line-height: 14px; |
||||
} |
||||
} |
||||
|
||||
&.open .dropdown-toggle { |
||||
box-shadow: none; |
||||
} |
||||
} |
||||
|
||||
button:focus, button:active { |
||||
outline: none; |
||||
} |
||||
|
||||
.open > .dropdown-toggle.btn-default, |
||||
.btn-default:active:focus, .btn-default:active:focus, |
||||
.btn-default.focus, .btn-default:focus { |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorBackground'); |
||||
} |
||||
} |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
.card { |
||||
width: 100%; |
||||
padding-bottom: 30px; |
||||
height: 100%; |
||||
border: none; |
||||
background-color: transparent; |
||||
|
||||
&.card--not-full-height { |
||||
height: auto; |
||||
} |
||||
} |
||||
|
||||
.card-body { |
||||
padding: 30px; |
||||
height: 100%; |
||||
border-radius: 5px; |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorBackground'); |
||||
} |
||||
} |
||||
|
||||
.card__title { |
||||
margin-bottom: 30px; |
||||
text-transform: uppercase; |
||||
position: relative; |
||||
|
||||
&:not(:first-child) { |
||||
margin-top: 40px; |
||||
} |
||||
|
||||
.subhead { |
||||
text-transform: none; |
||||
font-size: 12px; |
||||
line-height: 18px; |
||||
opacity: 0.7; |
||||
margin-top: 3px; |
||||
} |
||||
|
||||
* { |
||||
margin-bottom: 0; |
||||
} |
||||
|
||||
h5 { |
||||
font-size: 13px; |
||||
} |
||||
} |
||||
|
||||
.squared-corner-theme { |
||||
|
||||
.card-body { |
||||
border-radius: 0; |
||||
} |
||||
} |
||||
|
||||
.blocks-with-shadow-theme { |
||||
|
||||
.card-body { |
||||
box-shadow: 0 10px 30px 1px rgba(0, 0, 0, 0.06); |
||||
} |
||||
} |
@ -0,0 +1,200 @@
@@ -0,0 +1,200 @@
|
||||
.checkbox-btn { |
||||
display: flex; |
||||
cursor: pointer; |
||||
|
||||
&:hover { |
||||
|
||||
.checkbox-btn__checkbox-custom { |
||||
border-color: $color-accent; |
||||
} |
||||
|
||||
.checkbox-btn__label { |
||||
color: $color-accent; |
||||
} |
||||
} |
||||
|
||||
&.disabled { |
||||
pointer-events: none; |
||||
cursor: default; |
||||
|
||||
.checkbox-btn__checkbox-custom { |
||||
transition: 0s; |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
border-color: themed('colorFieldsBorder'); |
||||
} |
||||
|
||||
svg { |
||||
fill: #dddddd; |
||||
} |
||||
} |
||||
|
||||
.checkbox-btn__label { |
||||
color: #dddddd; |
||||
} |
||||
} |
||||
|
||||
&.checkbox-btn--colored { |
||||
|
||||
.checkbox-btn__checkbox-custom { |
||||
border-color: $color-accent; |
||||
background-color: $color-accent; |
||||
|
||||
svg { |
||||
fill: #ffffff; |
||||
} |
||||
} |
||||
|
||||
&.disabled { |
||||
opacity: 0.4; |
||||
|
||||
.checkbox-btn__checkbox-custom { |
||||
border-color: $color-accent; |
||||
background-color: $color-accent; |
||||
} |
||||
} |
||||
} |
||||
|
||||
&.checkbox-btn--colored-click { |
||||
|
||||
.checkbox-btn__checkbox:checked + .checkbox-btn__checkbox-custom { |
||||
border-color: $color-accent; |
||||
background-color: $color-accent; |
||||
|
||||
svg { |
||||
fill: #ffffff; |
||||
} |
||||
} |
||||
|
||||
&.disabled { |
||||
|
||||
.checkbox-btn__checkbox:checked + .checkbox-btn__checkbox-custom { |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
border-color: themed('colorFieldsBorder'); |
||||
} |
||||
|
||||
svg { |
||||
fill: #dddddd; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
&.checkbox-btn--button { |
||||
background: $color-accent; |
||||
min-width: 150px; |
||||
color: #ffffff; |
||||
height: 24px; |
||||
border-radius: 4px; |
||||
transition: all 0.3s; |
||||
padding: 0 6px; |
||||
width: 100%; |
||||
|
||||
.checkbox-btn__label { |
||||
color: #ffffff; |
||||
} |
||||
|
||||
.checkbox-btn__checkbox-custom { |
||||
display: none; |
||||
} |
||||
|
||||
.checkbox-btn__label-svg { |
||||
margin: auto 4px auto auto; |
||||
height: 16px; |
||||
line-height: 1; |
||||
|
||||
svg { |
||||
fill: #ffffff; |
||||
width: 14px; |
||||
height: 14px; |
||||
} |
||||
|
||||
.checkbox-btn__label-check { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
.checkbox-btn__checkbox:checked ~ .checkbox-btn__label-svg { |
||||
|
||||
.checkbox-btn__label-check { |
||||
display: block; |
||||
} |
||||
|
||||
.checkbox-btn__label-uncheck { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
.checkbox-btn__label { |
||||
margin: auto auto auto 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
&:hover { |
||||
background: $color-accent-hover; |
||||
|
||||
.checkbox-btn__label { |
||||
color: #ffffff; |
||||
} |
||||
} |
||||
|
||||
&.disabled { |
||||
opacity: 0.4; |
||||
|
||||
.checkbox-btn__label { |
||||
color: #ffffff; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.checkbox-btn__checkbox { |
||||
display: none; |
||||
} |
||||
|
||||
.checkbox-btn__checkbox-custom { |
||||
position: absolute; |
||||
width: 18px; |
||||
height: 18px; |
||||
border-radius: 3px; |
||||
|
||||
@include themify($themes) { |
||||
border: 1px solid themed('colorIcon'); |
||||
} |
||||
|
||||
svg { |
||||
transition: all 0.3s; |
||||
opacity: 0; |
||||
height: 16px; |
||||
width: 16px; |
||||
fill: $color-accent; |
||||
margin-top: -6px; |
||||
} |
||||
} |
||||
|
||||
.checkbox-btn__label { |
||||
line-height: 18px; |
||||
padding-left: 28px; |
||||
padding-top: 2px; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
} |
||||
|
||||
.checkbox-btn__checkbox-custom, |
||||
.checkbox-btn__label { |
||||
display: inline-block; |
||||
vertical-align: middle; |
||||
transition: all 0.3s; |
||||
} |
||||
|
||||
.checkbox-btn__checkbox:checked + .checkbox-btn__checkbox-custom { |
||||
|
||||
svg { |
||||
opacity: 1; |
||||
} |
||||
} |
@ -0,0 +1,508 @@
@@ -0,0 +1,508 @@
|
||||
.form { |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
|
||||
input, textarea { |
||||
width: 100%; |
||||
padding: 5px 10px; |
||||
font-size: 12px; |
||||
height: 32px; |
||||
transition: border 0.3s; |
||||
background: transparent; |
||||
|
||||
&::-webkit-input-placeholder { |
||||
color: $color-additional; |
||||
} |
||||
&::-moz-placeholder { |
||||
color: $color-additional; |
||||
} |
||||
/* Firefox 19+ */ |
||||
&:-moz-placeholder { |
||||
color: $color-additional; |
||||
} |
||||
/* Firefox 18- */ |
||||
&:-ms-input-placeholder { |
||||
color: $color-additional; |
||||
} |
||||
|
||||
@include themify($themes) { |
||||
border: 1px solid themed('colorFieldsBorder'); |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&[disabled] { |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorBackgroundBody'); |
||||
} |
||||
|
||||
&:focus, &:active { |
||||
|
||||
@include themify($themes) { |
||||
border-color: themed('colorBorder'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
&:focus, &:active { |
||||
outline: none; |
||||
border-color: $color-accent; |
||||
} |
||||
} |
||||
|
||||
textarea { |
||||
min-height: 85px; |
||||
} |
||||
|
||||
&.form--horizontal { |
||||
|
||||
.form__form-group { |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
} |
||||
|
||||
.form__form-group-field { |
||||
width: calc(100% - 80px); |
||||
padding-left: 10px; |
||||
margin-left: 80px; |
||||
} |
||||
|
||||
.form__form-group-label { |
||||
width: 80px; |
||||
max-height: 32px; |
||||
line-height: 18px; |
||||
margin: auto 0; |
||||
|
||||
& + .form__form-group-field { |
||||
margin-left: 0; |
||||
} |
||||
} |
||||
|
||||
.form__form-group-description { |
||||
margin-left: 90px; |
||||
} |
||||
|
||||
.form__button-toolbar { |
||||
margin-left: 0; |
||||
} |
||||
|
||||
.form__form-group-input-wrap--error-above { |
||||
margin-bottom: 15px; |
||||
|
||||
.form__form-group-error { |
||||
top: -28px; |
||||
} |
||||
} |
||||
|
||||
@media screen and (min-width: 480px) { |
||||
|
||||
.form__form-group-label { |
||||
width: 120px; |
||||
} |
||||
|
||||
.form__form-group-field { |
||||
width: calc(100% - 120px); |
||||
margin-left: 120px; |
||||
padding-left: 20px; |
||||
} |
||||
|
||||
.form__form-group-description, .form__button-toolbar { |
||||
margin-left: 140px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
&.form--preview { |
||||
display: flex; |
||||
|
||||
& > div:nth-child(2) { |
||||
margin-right: 50px; |
||||
} |
||||
|
||||
.form__form-group { |
||||
margin-bottom: 10px; |
||||
width: auto; |
||||
min-height: 18px; |
||||
} |
||||
|
||||
.form__select-color { |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
max-width: 84px; |
||||
margin-right: 40px; |
||||
} |
||||
|
||||
p { |
||||
margin-bottom: 10px; |
||||
} |
||||
} |
||||
|
||||
&.form--justify { |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
|
||||
.form__form-group { |
||||
width: 33.3333%; |
||||
} |
||||
|
||||
.form__button-toolbar { |
||||
width: 100%; |
||||
} |
||||
} |
||||
|
||||
.form__form-group-select { |
||||
width: 100%; |
||||
height: 32px; |
||||
font-size: 12px; |
||||
|
||||
.Select-control { |
||||
height: 32px; |
||||
border-radius: 0; |
||||
transition: all 0.3s; |
||||
background: transparent; |
||||
|
||||
@include themify($themes) { |
||||
border: 1px solid themed('colorFieldsBorder'); |
||||
} |
||||
} |
||||
|
||||
.Select-placeholder, .Select-input { |
||||
height: 30px; |
||||
} |
||||
|
||||
.Select-input { |
||||
width: 100%; |
||||
} |
||||
|
||||
input { |
||||
width: 100% !important; |
||||
border: none; |
||||
padding: 0; |
||||
} |
||||
|
||||
.Select-multi-value-wrapper { |
||||
|
||||
.Select-input { |
||||
width: inherit; |
||||
} |
||||
|
||||
.Select-value { |
||||
background: transparent; |
||||
border-color: $color-blue; |
||||
} |
||||
|
||||
.Select-value-label { |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText') !important; |
||||
} |
||||
} |
||||
|
||||
.Select-value-icon { |
||||
border-color: $color-blue; |
||||
} |
||||
} |
||||
|
||||
.Select-menu-outer { |
||||
top: calc(100% + 1px); |
||||
border-radius: 0; |
||||
box-shadow: none; |
||||
font-size: 12px; |
||||
animation: open 0.3s ease-in-out; |
||||
overflow: hidden; |
||||
|
||||
@include themify($themes) { |
||||
border: 1px solid themed('colorBorder'); |
||||
background: themed('colorBackground'); |
||||
} |
||||
} |
||||
|
||||
@keyframes open { |
||||
0% { |
||||
max-height: 0 |
||||
} |
||||
100% { |
||||
max-height: 200px |
||||
} |
||||
} |
||||
|
||||
.Select-option { |
||||
transition: all 0.3s; |
||||
border-radius: 0; |
||||
display: flex; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorBackground'); |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&.is-focused { |
||||
@include themify($themes) { |
||||
background: themed('colorHover'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
&.is-focused, &.is-focused:not(.is-open) { |
||||
|
||||
.Select-control { |
||||
border-color: $color-accent; |
||||
box-shadow: none; |
||||
background: transparent; |
||||
} |
||||
} |
||||
|
||||
.form__form-group-select-color { |
||||
display: block; |
||||
border-radius: 50%; |
||||
height: 10px; |
||||
width: 10px; |
||||
margin: auto 0 auto 5px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.form__half { |
||||
width: calc(50% - 15px); |
||||
height: 100%; |
||||
|
||||
&:first-child { |
||||
margin-right: 30px; |
||||
} |
||||
|
||||
.form__button-toolbar { |
||||
float: right; |
||||
} |
||||
|
||||
@media screen and (max-width: 1200px) { |
||||
width: 100%; |
||||
margin-right: 0; |
||||
} |
||||
} |
||||
|
||||
.form__form-group { |
||||
margin-bottom: 20px; |
||||
width: 100%; |
||||
position: relative; |
||||
} |
||||
|
||||
.form__form-group--address { |
||||
|
||||
input:last-child { |
||||
margin-left: 15px; |
||||
width: 70%; |
||||
} |
||||
} |
||||
|
||||
.form__form-group-field { |
||||
width: 100%; |
||||
display: flex; |
||||
margin: auto; |
||||
} |
||||
|
||||
.form__form-group-label { |
||||
margin-bottom: 4px; |
||||
display: inline-block; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
span { |
||||
color: #dddddd; |
||||
} |
||||
} |
||||
|
||||
.form__form-group-button { |
||||
padding: 6px; |
||||
height: 32px; |
||||
cursor: pointer; |
||||
transition: all 0.3s; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorFieldsBorder'); |
||||
border: 1px solid themed('colorFieldsBorder'); |
||||
} |
||||
|
||||
svg { |
||||
fill: $color-additional; |
||||
width: 18px; |
||||
height: 18px; |
||||
transition: all 0.3s; |
||||
} |
||||
|
||||
&.active { |
||||
background: $color-accent; |
||||
border: 1px solid $color-accent; |
||||
|
||||
svg { |
||||
fill: #ffffff; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.form__form-group-file { |
||||
|
||||
label { |
||||
border-radius: 2px; |
||||
line-height: 18px; |
||||
font-size: 12px; |
||||
padding: 4px 20px; |
||||
cursor: pointer; |
||||
transition: all 0.3s; |
||||
text-align: center; |
||||
|
||||
@include themify($themes) { |
||||
border: 1px solid themed('colorFieldsBorder'); |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&:hover { |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorFieldsBorder'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
span { |
||||
padding-left: 10px; |
||||
} |
||||
|
||||
input { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
.form__form-group-icon { |
||||
padding: 6px; |
||||
height: 32px; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorFieldsBorder'); |
||||
border: 1px solid themed('colorFieldsBorder'); |
||||
} |
||||
|
||||
svg { |
||||
fill: $color-additional; |
||||
width: 18px; |
||||
height: 18px; |
||||
transition: all 0.3s; |
||||
} |
||||
} |
||||
|
||||
.form__form-group-description { |
||||
font-size: 10px; |
||||
color: $color-additional; |
||||
line-height: 13px; |
||||
margin-top: 2px; |
||||
} |
||||
|
||||
.form__button-toolbar { |
||||
margin-top: 10px; |
||||
} |
||||
|
||||
.form__form-group-input-wrap { |
||||
width: 100%; |
||||
} |
||||
|
||||
.form__form-group-error { |
||||
font-size: 10px; |
||||
line-height: 13px; |
||||
color: #ad4444; |
||||
margin-bottom: -5px; |
||||
display: block; |
||||
margin-top: 5px; |
||||
} |
||||
|
||||
.form__form-group-input-wrap--error-above { |
||||
|
||||
.form__form-group-error { |
||||
position: absolute; |
||||
margin: 0; |
||||
right: 0; |
||||
top: 0; |
||||
padding: 5px 10px; |
||||
background: #ffbcbc; |
||||
border-radius: 3px; |
||||
|
||||
&:after { |
||||
content: ''; |
||||
position: absolute; |
||||
right: 10px; |
||||
bottom: -8px; |
||||
border: 4px solid transparent; |
||||
border-top: 4px solid #ffbcbc; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.form__form-group-date-cvc { |
||||
display: flex; |
||||
width: 100%; |
||||
|
||||
.form__form-group-date { |
||||
width: 100%; |
||||
margin-right: 20px; |
||||
} |
||||
|
||||
.form__form-group-cvc { |
||||
max-width: 280px; |
||||
width: 100%; |
||||
} |
||||
|
||||
@media screen and (max-width: 767px) { |
||||
flex-wrap: wrap; |
||||
|
||||
.form__form-group-date { |
||||
margin-right: 0; |
||||
} |
||||
|
||||
.form__form-group-cvc { |
||||
max-width: 100%; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.form__form-group-id-category { |
||||
width: 100%; |
||||
display: flex; |
||||
|
||||
.form__form-group-id { |
||||
min-width: 100px; |
||||
width: 40%; |
||||
margin-right: 20px; |
||||
} |
||||
|
||||
@media screen and (max-width: 767px) { |
||||
flex-wrap: wrap; |
||||
|
||||
.form__form-group-id { |
||||
margin-right: 0; |
||||
width: 100%; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.form__form-group-price-discount { |
||||
width: 100%; |
||||
display: flex; |
||||
margin-bottom: 20px; |
||||
|
||||
& > div { |
||||
margin-bottom: 0; |
||||
} |
||||
|
||||
.form__form-group-price { |
||||
margin-right: 20px; |
||||
} |
||||
|
||||
@media screen and (max-width: 767px) { |
||||
flex-wrap: wrap; |
||||
|
||||
.form__form-group-price { |
||||
margin-right: 0; |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
.load { |
||||
height: 100vh; |
||||
width: 100vw; |
||||
display: flex; |
||||
align-items: center; |
||||
position: fixed; |
||||
background: #ffffff; |
||||
z-index: 1000; |
||||
|
||||
& + div { |
||||
height: 100vh; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
&.loaded { |
||||
animation: ease loaded 0.5s; |
||||
} |
||||
} |
||||
|
||||
.load__icon { |
||||
animation: linear load 2s infinite; |
||||
margin: auto; |
||||
width: 32px; |
||||
height: 32px; |
||||
} |
||||
|
||||
.load__icon-wrap { |
||||
margin: auto; |
||||
} |
||||
|
||||
@keyframes load { |
||||
from { |
||||
transform: rotate(0deg) scale(2); |
||||
} |
||||
to { |
||||
transform: rotate(360deg) scale(2); |
||||
} |
||||
} |
||||
|
||||
@keyframes loaded { |
||||
from { |
||||
opacity: 1; |
||||
} |
||||
to { |
||||
opacity: 0; |
||||
} |
||||
} |
@ -0,0 +1,390 @@
@@ -0,0 +1,390 @@
|
||||
.sidebar { |
||||
position: fixed; |
||||
top: 0; |
||||
left: 0; |
||||
z-index: 99; |
||||
height: 100vh; |
||||
width: 240px; |
||||
box-shadow: 0 1px 30px 1px rgba(0, 0, 0, 0.11); |
||||
padding-top: 60px; |
||||
transition: transform 0.3s, width 0.3s; |
||||
transform: translateX(calc(-100% - 20px)); |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorBackground'); |
||||
} |
||||
|
||||
a { |
||||
display: block; |
||||
} |
||||
|
||||
&.sidebar--show { |
||||
transform: translateX(0); |
||||
|
||||
.sidebar__back { |
||||
display: block; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__back { |
||||
height: 100%; |
||||
width: 100vw; |
||||
position: absolute; |
||||
display: none; |
||||
background: transparent; |
||||
border: none; |
||||
} |
||||
|
||||
.sidebar__link-active { |
||||
|
||||
.sidebar__link { |
||||
|
||||
&:before { |
||||
opacity: 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__scroll { |
||||
width: 240px; |
||||
|
||||
& > div { |
||||
height: calc(100vh - 60px); |
||||
} |
||||
|
||||
.scrollbar-track { |
||||
|
||||
&.scrollbar-track-y { |
||||
width: 2px; |
||||
margin-right: 3px; |
||||
} |
||||
|
||||
&.scrollbar-track-x { |
||||
display: none !important; |
||||
} |
||||
} |
||||
|
||||
.scrollbar-thumb { |
||||
opacity: 0.3; |
||||
width: 5px; |
||||
} |
||||
} |
||||
|
||||
.sidebar__content { |
||||
padding-top: 15px; |
||||
height: 100%; |
||||
overflow: auto; |
||||
|
||||
& > div:last-child { |
||||
width: 4px !important; |
||||
|
||||
div { |
||||
transition: height 0.3s; |
||||
opacity: 0.52; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__block { |
||||
padding: 15px 0; |
||||
|
||||
@include themify($themes) { |
||||
border-bottom: 1px solid themed('colorBorder'); |
||||
} |
||||
|
||||
&:last-child { |
||||
border: none; |
||||
} |
||||
} |
||||
|
||||
.sidebar__link { |
||||
height: 36px; |
||||
width: 240px; |
||||
transition: all 0.3s; |
||||
position: relative; |
||||
cursor: pointer; |
||||
display: flex; |
||||
padding: 11px 20px; |
||||
overflow: hidden; |
||||
background: transparent; |
||||
border: none; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&:before { |
||||
content: ""; |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
height: 100%; |
||||
width: 2px; |
||||
background: $color-accent; |
||||
opacity: 0; |
||||
transition: all 0.3s; |
||||
} |
||||
|
||||
p { |
||||
position: absolute; |
||||
left: 43px; |
||||
width: 160px; |
||||
transition: left 0.3s; |
||||
top: 50%; |
||||
transform: translateY(-50%); |
||||
} |
||||
|
||||
&:hover { |
||||
text-decoration: none; |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
|
||||
&:before { |
||||
opacity: 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__link-title { |
||||
margin: 0; |
||||
font-size: 14px; |
||||
line-height: 16px; |
||||
position: relative; |
||||
display: flex; |
||||
} |
||||
|
||||
.sidebar__link-icon { |
||||
margin-right: 10px; |
||||
font-size: 13px; |
||||
line-height: 13px; |
||||
color: #b1c3c8; |
||||
} |
||||
|
||||
.sidebar__submenu { |
||||
transition: height 0.5s 0s, padding 0.5s 0s, opacity 0.4s 0.1s; |
||||
padding: 15px 0; |
||||
|
||||
& .sidebar__submenu { |
||||
margin-bottom: 0; |
||||
padding-bottom: 0; |
||||
padding-top: 0; |
||||
|
||||
.sidebar__link { |
||||
padding-left: 53px; |
||||
|
||||
p { |
||||
left: 53px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
|
||||
.sidebar__link { |
||||
padding-left: 43px; |
||||
|
||||
&:hover { |
||||
@include themify($themes) { |
||||
background-color: themed('colorBackground'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__link { |
||||
display: block; |
||||
} |
||||
} |
||||
|
||||
.sidebar__category-icon { |
||||
position: absolute; |
||||
right: 15px; |
||||
font-size: 10px; |
||||
line-height: 14px; |
||||
opacity: 1; |
||||
transition: opacity 0.5s 0.2s, transform 0.3s; |
||||
color: $color-gray; |
||||
|
||||
} |
||||
|
||||
.sidebar__link-badge { |
||||
width: 26px; |
||||
height: 14px; |
||||
background-color: $color-red; |
||||
font-size: 8px; |
||||
font-weight: 400; |
||||
padding: 2px; |
||||
margin-left: 5px; |
||||
line-height: 9px; |
||||
position: relative; |
||||
text-transform: uppercase; |
||||
border-radius: 7px; |
||||
|
||||
span { |
||||
position: absolute; |
||||
left: 0; |
||||
top: 3px; |
||||
width: 26px; |
||||
text-align: center; |
||||
} |
||||
} |
||||
|
||||
.sidebar__wrapper--desktop { |
||||
display: none; |
||||
} |
||||
|
||||
.sidebar__category-wrap { |
||||
|
||||
&.sidebar__category-wrap--open { |
||||
|
||||
.sidebar__category-icon { |
||||
transform: rotate(90deg); |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__category-new { |
||||
height: 6px; |
||||
width: 6px; |
||||
border-radius: 50%; |
||||
top: -3px; |
||||
display: block; |
||||
margin-left: 5px; |
||||
background: $color-red; |
||||
} |
||||
|
||||
@media screen and (min-width: 576px) { |
||||
|
||||
.sidebar { |
||||
transform: translateX(0); |
||||
|
||||
&.sidebar--no-desktop { |
||||
transform: translateX(calc(-100% - 20px)); |
||||
|
||||
&.sidebar--show { |
||||
transform: translateX(0); |
||||
} |
||||
|
||||
.sidebar__wrapper--mobile { |
||||
display: block; |
||||
} |
||||
} |
||||
|
||||
&.sidebar--collapse { |
||||
width: 55px; |
||||
overflow: visible; |
||||
|
||||
.sidebar__scroll, .sidebar__content { |
||||
width: 55px; |
||||
overflow: visible !important; |
||||
transition: width 0.3s; |
||||
} |
||||
|
||||
.sidebar__submenu { |
||||
padding: 0 0 15px 0; |
||||
transition: 0s; |
||||
} |
||||
|
||||
.sidebar__category-wrap { |
||||
|
||||
&:hover { |
||||
|
||||
.sidebar__category { |
||||
width: 240px; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorHover'); |
||||
} |
||||
|
||||
&:before { |
||||
opacity: 1; |
||||
} |
||||
} |
||||
|
||||
.sidebar__submenu-wrap { |
||||
width: 185px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__submenu-wrap { |
||||
position: absolute; |
||||
left: 55px; |
||||
width: 0; |
||||
transition: 0.3s; |
||||
overflow: hidden; |
||||
|
||||
.sidebar__link { |
||||
width: 185px; |
||||
padding-left: 15px; |
||||
|
||||
p { |
||||
position: relative; |
||||
left: 0; |
||||
animation: none; |
||||
} |
||||
} |
||||
|
||||
.sidebar__submenu-wrap { |
||||
position: relative; |
||||
left: 0; |
||||
|
||||
.sidebar__link { |
||||
padding-left: 30px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__link { |
||||
overflow: hidden; |
||||
width: 55px; |
||||
background-color: transparent; |
||||
|
||||
p { |
||||
position: absolute; |
||||
left: 70px; |
||||
width: 160px; |
||||
} |
||||
|
||||
&:hover { |
||||
width: 240px; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorHover'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
.sidebar__category-icon { |
||||
opacity: 0; |
||||
transition: opacity 0s; |
||||
} |
||||
|
||||
.scrollbar-track.scrollbar-track-y { |
||||
margin-right: 188px; |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
.sidebar__wrapper--desktop { |
||||
display: block; |
||||
} |
||||
|
||||
.sidebar__wrapper--mobile { |
||||
display: none; |
||||
} |
||||
|
||||
} |
||||
|
||||
@media screen and (min-width: 992px) { |
||||
|
||||
.sidebar.sidebar--no-desktop { |
||||
transform: translateX(0); |
||||
display: none; |
||||
} |
||||
} |
@ -0,0 +1,803 @@
@@ -0,0 +1,803 @@
|
||||
.topbar { |
||||
width: 100%; |
||||
position: fixed; |
||||
top: 0; |
||||
height: 60px; |
||||
z-index: 101; |
||||
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.05); |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorBackground'); |
||||
} |
||||
} |
||||
|
||||
.topbar__wrapper { |
||||
position: relative; |
||||
display: flex; |
||||
height: 60px; |
||||
} |
||||
|
||||
.topbar__button { |
||||
width: 60px; |
||||
height: 60px; |
||||
display: flex; |
||||
background: transparent; |
||||
border: none; |
||||
cursor: pointer; |
||||
transition: 0.3s; |
||||
|
||||
&:focus { |
||||
outline: none; |
||||
} |
||||
|
||||
&:hover { |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorHover'); |
||||
} |
||||
} |
||||
|
||||
&.topbar__button--desktop { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
.topbar__button-icon { |
||||
margin: auto; |
||||
transition: all 0.3s; |
||||
width: 16px; |
||||
} |
||||
|
||||
.topbar__logo { |
||||
width: 150px; |
||||
height: 32px; |
||||
margin: auto 0; |
||||
background-repeat: no-repeat; |
||||
background-position-y: center; |
||||
background-position-x: left; |
||||
background-size: contain; |
||||
display: none; |
||||
|
||||
@include themify($themes) { |
||||
background-image: themed('logoImg'); |
||||
} |
||||
|
||||
@media screen and (min-width: 768px) { |
||||
display: block; |
||||
} |
||||
} |
||||
|
||||
.topbar__right { |
||||
position: absolute; |
||||
right: 0; |
||||
display: flex; |
||||
height: 100%; |
||||
margin-right: 15px; |
||||
} |
||||
|
||||
.topbar__left { |
||||
position: absolute; |
||||
left: 0; |
||||
display: flex; |
||||
height: 100%; |
||||
width: 50%; |
||||
} |
||||
|
||||
.topbar__avatar { |
||||
height: 100%; |
||||
display: flex; |
||||
cursor: pointer; |
||||
position: relative; |
||||
border-radius: 0; |
||||
border: none; |
||||
transition: all 0.3s; |
||||
box-shadow: none; |
||||
padding: 0 15px; |
||||
background-color: transparent; |
||||
|
||||
&:hover, &:focus, &:active, &:focus:active { |
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
} |
||||
|
||||
&:focus { |
||||
outline: none; |
||||
} |
||||
|
||||
&:before { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
.topbar__avatar-img, .topbar__avatar-name, .topbar__icon { |
||||
margin: auto 0; |
||||
} |
||||
|
||||
.topbar__avatar-img { |
||||
border-radius: 50%; |
||||
height: 36px; |
||||
width: 36px; |
||||
} |
||||
|
||||
.topbar__avatar-name { |
||||
font-size: 13px; |
||||
line-height: 18px; |
||||
font-weight: 400; |
||||
display: none; |
||||
margin-left: 10px; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
} |
||||
|
||||
.topbar__icon { |
||||
margin-left: 8px; |
||||
height: 18px; |
||||
margin-top: auto; |
||||
fill: #b1c3c8; |
||||
} |
||||
|
||||
.topbar__menu { |
||||
width: 200px; |
||||
border-radius: 0; |
||||
border: none; |
||||
padding: 15px 0; |
||||
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.05); |
||||
margin-top: 0; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorBackground'); |
||||
} |
||||
|
||||
button { |
||||
padding: 0; |
||||
|
||||
&:hover { |
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
*:focus { |
||||
outline: none; |
||||
} |
||||
} |
||||
|
||||
.topbar__menu-wrap { |
||||
z-index: 101; |
||||
position: absolute; |
||||
width: 100%; |
||||
padding: 0 10px; |
||||
min-width: 220px; |
||||
right: 0; |
||||
} |
||||
|
||||
.topbar__link { |
||||
display: flex; |
||||
padding: 9px 20px; |
||||
transition: all 0.3s; |
||||
height: 32px; |
||||
width: 100%; |
||||
position: relative; |
||||
cursor: pointer; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&:before { |
||||
content: ""; |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
height: 100%; |
||||
width: 2px; |
||||
background: $color-accent; |
||||
opacity: 0; |
||||
transition: all 0.3s; |
||||
} |
||||
|
||||
&:hover { |
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
|
||||
&:before { |
||||
opacity: 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.topbar__link-title { |
||||
margin: 0; |
||||
font-size: 14px; |
||||
line-height: 16px; |
||||
} |
||||
|
||||
.topbar__link-icon { |
||||
margin-right: 10px; |
||||
font-size: 13px; |
||||
line-height: 13px; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorIcon'); |
||||
} |
||||
} |
||||
|
||||
.topbar__menu-divider { |
||||
margin: 15px 0; |
||||
|
||||
@include themify($themes) { |
||||
border-top: 1px solid themed('colorBorder'); |
||||
} |
||||
} |
||||
|
||||
.topbar__profile { |
||||
margin-bottom: 0; |
||||
margin-left: 20px; |
||||
position: relative; |
||||
} |
||||
|
||||
.topbar__collapse { |
||||
position: relative; |
||||
display: none; |
||||
|
||||
&.topbar__collapse--language { |
||||
min-width: 70px; |
||||
display: block; |
||||
|
||||
& > button { |
||||
padding: 0 4px; |
||||
width: 100%; |
||||
} |
||||
} |
||||
|
||||
@media screen and (min-width: 568px) { |
||||
display: block; |
||||
} |
||||
} |
||||
|
||||
.topbar__collapse-content { |
||||
width: 270px; |
||||
position: absolute; |
||||
right: 0; |
||||
bottom: 20px; |
||||
transform: translateY(100%); |
||||
box-shadow: 0 10px 25px 0 rgba(33, 36, 50, 0.13); |
||||
z-index: 101; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorBackground'); |
||||
} |
||||
|
||||
&.topbar__collapse-content--language { |
||||
max-width: 70px; |
||||
padding: 10px 0; |
||||
width: 100%; |
||||
} |
||||
|
||||
@media screen and (max-width: 768px) { |
||||
left: 50%; |
||||
transform: translate(-50%, 100%); |
||||
} |
||||
|
||||
@media screen and (min-width: 520px) { |
||||
width: 330px; |
||||
} |
||||
} |
||||
|
||||
.topbar__language-btn { |
||||
padding: 4px 15px; |
||||
width: 100%; |
||||
border: none; |
||||
background: transparent; |
||||
cursor: pointer; |
||||
text-align: left; |
||||
font-size: 13px; |
||||
line-height: 16px; |
||||
transition: 0.3s; |
||||
|
||||
&:hover { |
||||
color: $color-accent; |
||||
} |
||||
} |
||||
|
||||
.topbar__language-btn-title { |
||||
display: flex; |
||||
font-size: 11px; |
||||
align-items: center; |
||||
margin: auto 0; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&:not(:last-child) { |
||||
margin-right: 5px; |
||||
} |
||||
|
||||
img { |
||||
height: 11px; |
||||
width: 16px; |
||||
margin-right: 4px; |
||||
} |
||||
} |
||||
|
||||
.topbar__back { |
||||
position: fixed; |
||||
width: 100vw; |
||||
height: 100vh; |
||||
top: 0; |
||||
bottom: 0; |
||||
left: 0; |
||||
right: 0; |
||||
z-index: 100; |
||||
background: transparent; |
||||
border: none; |
||||
} |
||||
|
||||
.topbar__collapse-title-wrap { |
||||
padding: 20px 15px 15px 15px; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
|
||||
@include themify($themes) { |
||||
border-bottom: 1px solid themed('colorBorder'); |
||||
} |
||||
} |
||||
|
||||
.topbar__collapse-item { |
||||
padding: 12px 55px 12px 70px; |
||||
display: flex; |
||||
position: relative; |
||||
height: 62px; |
||||
align-items: center; |
||||
flex-wrap: wrap; |
||||
|
||||
@include themify($themes) { |
||||
border-bottom: 1px solid themed('colorBorder'); |
||||
} |
||||
} |
||||
|
||||
.topbar__collapse-img-wrap { |
||||
height: 40px; |
||||
width: 40px; |
||||
border-radius: 50%; |
||||
overflow: hidden; |
||||
position: absolute; |
||||
left: 15px; |
||||
} |
||||
|
||||
.topbar__collapse-message { |
||||
margin: 0; |
||||
font-size: 12px; |
||||
line-height: 16px; |
||||
color: $color-gray; |
||||
|
||||
&.topbar__collapse-message--mail { |
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
white-space: nowrap; |
||||
} |
||||
} |
||||
|
||||
.topbar__collapse-name { |
||||
margin: 0; |
||||
font-size: 12px; |
||||
line-height: 16px; |
||||
color: $color-accent; |
||||
} |
||||
|
||||
.topbar__collapse-date { |
||||
position: absolute; |
||||
top: 12px; |
||||
right: 15px; |
||||
font-size: 10px; |
||||
color: $color-gray; |
||||
margin-top: 2px; |
||||
} |
||||
|
||||
.topbar__collapse-link { |
||||
display: block; |
||||
padding: 10px; |
||||
text-transform: uppercase; |
||||
color: $color-accent; |
||||
transition: 0.3s; |
||||
text-align: center; |
||||
font-weight: 500; |
||||
font-size: 10px; |
||||
line-height: 16px; |
||||
|
||||
&:hover { |
||||
color: $color-accent-hover; |
||||
} |
||||
} |
||||
|
||||
.topbar__collapse-title { |
||||
font-size: 14px; |
||||
line-height: 16px; |
||||
} |
||||
|
||||
.topbar__collapse-button { |
||||
color: #c5d2d6; |
||||
border: none; |
||||
padding: 0; |
||||
text-align: right; |
||||
font-size: 12px; |
||||
line-height: 16px; |
||||
transition: 0.3s; |
||||
background: transparent; |
||||
|
||||
&:hover { |
||||
color: $color-accent; |
||||
} |
||||
} |
||||
|
||||
.topbar__btn { |
||||
font-size: 18px; |
||||
height: 100%; |
||||
padding: 0 10px; |
||||
cursor: pointer; |
||||
position: relative; |
||||
display: flex; |
||||
border: none; |
||||
background: transparent; |
||||
transition: all 0.3s; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&:hover { |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
} |
||||
|
||||
svg { |
||||
margin: auto; |
||||
height: 18px; |
||||
width: 18px; |
||||
fill: #b1c3c8; |
||||
} |
||||
|
||||
&.topbar__btn--new { |
||||
|
||||
.topbar__btn-new-label { |
||||
position: absolute; |
||||
right: 9px; |
||||
top: 20px; |
||||
|
||||
& > div { |
||||
position: relative; |
||||
|
||||
&:before { |
||||
background-color: rgba(224, 83, 111, 0.2); |
||||
content: ""; |
||||
position: absolute; |
||||
top: 50%; |
||||
left: 50%; |
||||
border-radius: 50%; |
||||
transform: translate(-50%, -50%); |
||||
animation: beforePulse 1.5s infinite; |
||||
} |
||||
|
||||
&:after { |
||||
height: 7px; |
||||
width: 7px; |
||||
background-color: #e0536f; |
||||
content: ""; |
||||
position: absolute; |
||||
top: 50%; |
||||
left: 50%; |
||||
border-radius: 50%; |
||||
transform: translate(-50%, -50%); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@keyframes beforePulse { |
||||
from { |
||||
width: 7px; |
||||
height: 7px; |
||||
} |
||||
25% { |
||||
width: 13px; |
||||
height: 13px; |
||||
} |
||||
to { |
||||
width: 7px; |
||||
height: 7px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.topbar__nav { |
||||
width: 100%; |
||||
display: none; |
||||
height: 100%; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
|
||||
.topbar .topbar__nav-dropdown-toggle { |
||||
height: 60px; |
||||
background: transparent; |
||||
border-radius: 0; |
||||
border: none; |
||||
font-size: 14px; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
margin: 0; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&:before { |
||||
display: none; |
||||
} |
||||
|
||||
&:hover, &:focus, &:active, &:focus:active { |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
} |
||||
|
||||
svg { |
||||
fill: $color-additional; |
||||
margin-left: 3px; |
||||
height: 16px; |
||||
width: 16px; |
||||
margin-right: 0; |
||||
} |
||||
} |
||||
|
||||
.topbar__nav-dropdown-menu { |
||||
width: 240px; |
||||
border-top: 2px solid $color-accent; |
||||
|
||||
button { |
||||
padding: 0; |
||||
} |
||||
} |
||||
|
||||
.topbar__link-badge { |
||||
width: 26px; |
||||
height: 14px; |
||||
background-color: $color-red; |
||||
font-size: 8px; |
||||
font-weight: 400; |
||||
padding: 2px; |
||||
margin-left: 5px; |
||||
line-height: 9px; |
||||
position: relative; |
||||
text-transform: uppercase; |
||||
border-radius: 7px; |
||||
|
||||
span { |
||||
position: absolute; |
||||
left: 0; |
||||
top: 3px; |
||||
width: 26px; |
||||
text-align: center; |
||||
} |
||||
} |
||||
|
||||
.topbar__nav-link { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
height: 60px; |
||||
padding: 10px 25px; |
||||
transition: 0.3s; |
||||
font-size: 14px; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorText'); |
||||
} |
||||
|
||||
&:hover { |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
.topbar__category-wrap { |
||||
position: relative; |
||||
|
||||
&:hover { |
||||
|
||||
.topbar__submenu { |
||||
opacity: 1; |
||||
width: auto; |
||||
height: auto; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.topbar__category-icon { |
||||
position: absolute; |
||||
right: 20px; |
||||
font-size: 10px; |
||||
line-height: 14px; |
||||
|
||||
@include themify($themes) { |
||||
color: themed('colorIcon'); |
||||
} |
||||
} |
||||
|
||||
.topbar__submenu { |
||||
position: absolute; |
||||
right: 1px; |
||||
top: 0; |
||||
transform: translateX(100%); |
||||
transition: 0.3s; |
||||
opacity: 0; |
||||
width: 0; |
||||
height: 0; |
||||
overflow: hidden; |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorHover'); |
||||
} |
||||
|
||||
.topbar__link { |
||||
|
||||
&:hover { |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorBackground'); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.topbar__search { |
||||
display: none; |
||||
margin: auto 0; |
||||
padding: 0; |
||||
position: relative; |
||||
} |
||||
|
||||
.topbar__search-field { |
||||
width: 0; |
||||
transition: all 0.3s; |
||||
opacity: 0; |
||||
margin: auto auto auto 0; |
||||
border: none; |
||||
border-radius: 13px; |
||||
height: 26px; |
||||
padding-left: 10px; |
||||
padding-right: 46px; |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorBackgroundBody'); |
||||
} |
||||
|
||||
&.topbar__search-field--open { |
||||
width: 200px; |
||||
opacity: 1; |
||||
margin-left: 10px; |
||||
|
||||
& + button { |
||||
right: 10px; |
||||
} |
||||
} |
||||
|
||||
&:focus { |
||||
outline: none; |
||||
} |
||||
} |
||||
|
||||
.topbar__search-btn { |
||||
height: 26px; |
||||
width: 26px; |
||||
border-radius: 13px; |
||||
border: none; |
||||
padding: 0; |
||||
background: transparent; |
||||
position: absolute; |
||||
right: 0; |
||||
|
||||
&:hover { |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorBackgroundBody'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@media screen and (min-width: 480px) { |
||||
|
||||
.topbar__avatar-name { |
||||
display: block; |
||||
} |
||||
|
||||
.topbar__menu { |
||||
width: 100%; |
||||
left: 0 !important; |
||||
} |
||||
} |
||||
|
||||
@media screen and (min-width: 576px) { |
||||
|
||||
.topbar__button { |
||||
|
||||
&.topbar__button--desktop { |
||||
display: block; |
||||
} |
||||
|
||||
&.topbar__button--mobile { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
.topbar.topbar--navigation { |
||||
|
||||
.topbar__button.topbar__button--mobile { |
||||
display: block; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@media screen and (min-width: 768px) { |
||||
|
||||
.topbar__search { |
||||
display: flex; |
||||
} |
||||
} |
||||
|
||||
@media screen and (min-width: 992px) { |
||||
|
||||
.topbar__nav { |
||||
display: flex; |
||||
} |
||||
|
||||
.topbar.topbar--navigation { |
||||
|
||||
.topbar__logo { |
||||
margin-left: 15px; |
||||
display: block; |
||||
} |
||||
|
||||
.topbar__button.topbar__button--mobile { |
||||
display: none; |
||||
} |
||||
|
||||
.topbar__avatar-name { |
||||
display: none; |
||||
} |
||||
|
||||
.topbar__profile { |
||||
margin-left: 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@media screen and (min-width: 1200px) { |
||||
|
||||
.topbar.topbar--navigation { |
||||
|
||||
.topbar__avatar-name { |
||||
display: block; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@media screen and (min-width: 1580px) { |
||||
|
||||
.topbar__nav-dropdown-toggle, .topbar__nav-link { |
||||
width: 240px; |
||||
} |
||||
} |
@ -0,0 +1,246 @@
@@ -0,0 +1,246 @@
|
||||
.account { |
||||
height: 100vh; |
||||
width: 100%; |
||||
min-height: 100vh; |
||||
display: flex; |
||||
overflow-y: auto; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorBackgroundBody'); |
||||
} |
||||
|
||||
&.account--photo { |
||||
background: url(../shared/img/404/bg_404.png) no-repeat center; |
||||
background-size: cover; |
||||
|
||||
.account__card { |
||||
background-color: rgba(0, 10, 16, 0.8); |
||||
color: #ffffff; |
||||
} |
||||
|
||||
.form__form-group-icon, .form__form-group-button { |
||||
background: transparent; |
||||
} |
||||
|
||||
.form__form-group-icon, input:not(:last-child) { |
||||
border-right: none; |
||||
} |
||||
|
||||
.form__form-group-button, input { |
||||
border-left: none; |
||||
} |
||||
|
||||
.form__form-group-button.active { |
||||
border-color: #eff1f5; |
||||
|
||||
svg { |
||||
fill: $color-accent; |
||||
} |
||||
} |
||||
|
||||
input { |
||||
color: #ffffff; |
||||
|
||||
&:focus { |
||||
border-color: #eff1f5; |
||||
} |
||||
} |
||||
|
||||
p { |
||||
color: #ffffff; |
||||
} |
||||
|
||||
.account__title { |
||||
color: #ffffff; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.account__wrapper { |
||||
margin: auto; |
||||
padding: 10px; |
||||
} |
||||
|
||||
.account__card { |
||||
|
||||
@include themify($themes) { |
||||
background-color: themed('colorBackground'); |
||||
} |
||||
|
||||
background-color: #ffffff; |
||||
padding: 50px 60px; |
||||
max-width: 520px; |
||||
width: 100%; |
||||
} |
||||
|
||||
.account__profile { |
||||
text-align: center; |
||||
} |
||||
|
||||
.account__btns { |
||||
display: flex; |
||||
width: calc(100% + 10px); |
||||
margin: -10px 0 -20px -10px; |
||||
|
||||
a { |
||||
margin: 10px 0 20px 10px; |
||||
white-space: nowrap; |
||||
} |
||||
} |
||||
|
||||
.account__btn { |
||||
width: 100%; |
||||
margin-right: 0; |
||||
} |
||||
|
||||
.account__avatar { |
||||
height: 64px; |
||||
width: 64px; |
||||
border-radius: 50%; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.account__name { |
||||
font-size: 11px; |
||||
text-transform: uppercase; |
||||
font-weight: 700; |
||||
line-height: 15px; |
||||
margin-top: 5px; |
||||
} |
||||
|
||||
.account__sub { |
||||
margin-top: 0; |
||||
margin-bottom: 10px; |
||||
color: $color-additional; |
||||
font-size: 11px; |
||||
line-height: 15px; |
||||
} |
||||
|
||||
.account__forgot-password { |
||||
position: absolute; |
||||
font-size: 11px; |
||||
line-height: 15px; |
||||
bottom: -18px; |
||||
right: 0; |
||||
|
||||
a { |
||||
color: $color-blue; |
||||
|
||||
&:hover { |
||||
color: $color-blue-hover; |
||||
text-decoration: none; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.form__form-group--forgot { |
||||
margin-bottom: 40px; |
||||
} |
||||
|
||||
.account__or { |
||||
text-align: center; |
||||
margin-top: 35px; |
||||
margin-bottom: 20px; |
||||
position: relative; |
||||
|
||||
&:before, &:after { |
||||
content: ""; |
||||
height: 1px; |
||||
width: calc(50% - 90px); |
||||
background: #dddddd; |
||||
position: absolute; |
||||
top: 10px; |
||||
} |
||||
|
||||
&:before { |
||||
left: 0; |
||||
} |
||||
|
||||
&:after { |
||||
right: 0; |
||||
} |
||||
} |
||||
|
||||
.account__social { |
||||
text-align: center; |
||||
} |
||||
|
||||
.account__social-btn { |
||||
display: inline-block; |
||||
height: 38px; |
||||
width: 38px; |
||||
border-radius: 5px; |
||||
padding: 9px; |
||||
margin-right: 10px; |
||||
transition: all 0.3s; |
||||
border: none; |
||||
|
||||
&:last-child { |
||||
margin-right: 0; |
||||
} |
||||
|
||||
svg { |
||||
fill: #ffffff; |
||||
height: 20px; |
||||
width: 20px; |
||||
} |
||||
|
||||
&.account__social-btn--facebook { |
||||
background: #4766a4; |
||||
|
||||
&:hover { |
||||
background: darken(#4766a4, 10%); |
||||
} |
||||
} |
||||
|
||||
&.account__social-btn--google { |
||||
background: #c74d4d; |
||||
|
||||
&:hover { |
||||
background: darken(#c74d4d, 10%); |
||||
} |
||||
} |
||||
} |
||||
|
||||
.account__head { |
||||
margin-bottom: 30px; |
||||
padding-left: 10px; |
||||
border-left: 4px solid $color-blue; |
||||
} |
||||
|
||||
.account__logo { |
||||
font-weight: 700; |
||||
} |
||||
|
||||
.account__logo-accent { |
||||
color: $color-blue; |
||||
} |
||||
|
||||
.account__have-account { |
||||
text-align: center; |
||||
margin-top: 20px; |
||||
|
||||
a { |
||||
color: $color-blue; |
||||
transition: all 0.3s; |
||||
|
||||
&:hover { |
||||
color: $color-blue-hover; |
||||
text-decoration: none; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@media screen and (max-width: 520px) { |
||||
|
||||
.account__card { |
||||
padding: 35px 30px; |
||||
} |
||||
} |
||||
|
||||
@media screen and (max-width: 425px) { |
||||
|
||||
.account__btns { |
||||
flex-wrap: wrap; |
||||
} |
||||
} |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
html { |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
*, *:before, *:after { |
||||
box-sizing: inherit; |
||||
} |
@ -0,0 +1,447 @@
@@ -0,0 +1,447 @@
|
||||
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ |
||||
|
||||
/* Document |
||||
========================================================================== */ |
||||
|
||||
/** |
||||
* 1. Correct the line height in all browsers. |
||||
* 2. Prevent adjustments of font size after orientation changes in |
||||
* IE on Windows Phone and in iOS. |
||||
*/ |
||||
|
||||
html { |
||||
line-height: 1.15; /* 1 */ |
||||
-ms-text-size-adjust: 100%; /* 2 */ |
||||
-webkit-text-size-adjust: 100%; /* 2 */ |
||||
} |
||||
|
||||
/* Sections |
||||
========================================================================== */ |
||||
|
||||
/** |
||||
* Remove the margin in all browsers (opinionated). |
||||
*/ |
||||
|
||||
body { |
||||
margin: 0; |
||||
} |
||||
|
||||
/** |
||||
* Add the correct display in IE 9-. |
||||
*/ |
||||
|
||||
article, |
||||
aside, |
||||
footer, |
||||
header, |
||||
nav, |
||||
section { |
||||
display: block; |
||||
} |
||||
|
||||
/** |
||||
* Correct the font size and margin on `h1` elements within `section` and |
||||
* `article` contexts in Chrome, Firefox, and Safari. |
||||
*/ |
||||
|
||||
h1 { |
||||
font-size: 2em; |
||||
margin: 0.67em 0; |
||||
} |
||||
|
||||
/* Grouping content |
||||
========================================================================== */ |
||||
|
||||
/** |
||||
* Add the correct display in IE 9-. |
||||
* 1. Add the correct display in IE. |
||||
*/ |
||||
|
||||
figcaption, |
||||
figure, |
||||
main { /* 1 */ |
||||
display: block; |
||||
} |
||||
|
||||
/** |
||||
* Add the correct margin in IE 8. |
||||
*/ |
||||
|
||||
figure { |
||||
margin: 1em 40px; |
||||
} |
||||
|
||||
/** |
||||
* 1. Add the correct box sizing in Firefox. |
||||
* 2. Show the overflow in Edge and IE. |
||||
*/ |
||||
|
||||
hr { |
||||
box-sizing: content-box; /* 1 */ |
||||
height: 0; /* 1 */ |
||||
overflow: visible; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* 1. Correct the inheritance and scaling of font size in all browsers. |
||||
* 2. Correct the odd `em` font sizing in all browsers. |
||||
*/ |
||||
|
||||
pre { |
||||
font-family: monospace, monospace; /* 1 */ |
||||
font-size: 1em; /* 2 */ |
||||
} |
||||
|
||||
/* Text-level semantics |
||||
========================================================================== */ |
||||
|
||||
/** |
||||
* 1. Remove the gray background on active links in IE 10. |
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+. |
||||
*/ |
||||
|
||||
a { |
||||
background-color: transparent; /* 1 */ |
||||
-webkit-text-decoration-skip: objects; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* 1. Remove the bottom border in Chrome 57- and Firefox 39-. |
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. |
||||
*/ |
||||
|
||||
abbr[title] { |
||||
border-bottom: none; /* 1 */ |
||||
text-decoration: underline; /* 2 */ |
||||
text-decoration: underline dotted; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6. |
||||
*/ |
||||
|
||||
b, |
||||
strong { |
||||
font-weight: inherit; |
||||
} |
||||
|
||||
/** |
||||
* Add the correct font weight in Chrome, Edge, and Safari. |
||||
*/ |
||||
|
||||
b, |
||||
strong { |
||||
font-weight: bolder; |
||||
} |
||||
|
||||
/** |
||||
* 1. Correct the inheritance and scaling of font size in all browsers. |
||||
* 2. Correct the odd `em` font sizing in all browsers. |
||||
*/ |
||||
|
||||
code, |
||||
kbd, |
||||
samp { |
||||
font-family: monospace, monospace; /* 1 */ |
||||
font-size: 1em; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* Add the correct font style in Android 4.3-. |
||||
*/ |
||||
|
||||
dfn { |
||||
font-style: italic; |
||||
} |
||||
|
||||
/** |
||||
* Add the correct background and color in IE 9-. |
||||
*/ |
||||
|
||||
mark { |
||||
background-color: #ff0; |
||||
color: #000; |
||||
} |
||||
|
||||
/** |
||||
* Add the correct font size in all browsers. |
||||
*/ |
||||
|
||||
small { |
||||
font-size: 80%; |
||||
} |
||||
|
||||
/** |
||||
* Prevent `sub` and `sup` elements from affecting the line height in |
||||
* all browsers. |
||||
*/ |
||||
|
||||
sub, |
||||
sup { |
||||
font-size: 75%; |
||||
line-height: 0; |
||||
position: relative; |
||||
vertical-align: baseline; |
||||
} |
||||
|
||||
sub { |
||||
bottom: -0.25em; |
||||
} |
||||
|
||||
sup { |
||||
top: -0.5em; |
||||
} |
||||
|
||||
/* Embedded content |
||||
========================================================================== */ |
||||
|
||||
/** |
||||
* Add the correct display in IE 9-. |
||||
*/ |
||||
|
||||
audio, |
||||
video { |
||||
display: inline-block; |
||||
} |
||||
|
||||
/** |
||||
* Add the correct display in iOS 4-7. |
||||
*/ |
||||
|
||||
audio:not([controls]) { |
||||
display: none; |
||||
height: 0; |
||||
} |
||||
|
||||
/** |
||||
* Remove the border on images inside links in IE 10-. |
||||
*/ |
||||
|
||||
img { |
||||
border-style: none; |
||||
} |
||||
|
||||
/** |
||||
* Hide the overflow in IE. |
||||
*/ |
||||
|
||||
svg:not(:root) { |
||||
overflow: hidden; |
||||
} |
||||
|
||||
/* Forms |
||||
========================================================================== */ |
||||
|
||||
/** |
||||
* 1. Change the font styles in all browsers (opinionated). |
||||
* 2. Remove the margin in Firefox and Safari. |
||||
*/ |
||||
|
||||
button, |
||||
input, |
||||
optgroup, |
||||
select, |
||||
textarea { |
||||
font-family: sans-serif; /* 1 */ |
||||
font-size: 100%; /* 1 */ |
||||
line-height: 1.15; /* 1 */ |
||||
margin: 0; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* Show the overflow in IE. |
||||
* 1. Show the overflow in Edge. |
||||
*/ |
||||
|
||||
button, |
||||
input { /* 1 */ |
||||
overflow: visible; |
||||
} |
||||
|
||||
/** |
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE. |
||||
* 1. Remove the inheritance of text transform in Firefox. |
||||
*/ |
||||
|
||||
button, |
||||
select { /* 1 */ |
||||
text-transform: none; |
||||
} |
||||
|
||||
/** |
||||
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` |
||||
* controls in Android 4. |
||||
* 2. Correct the inability to style clickable types in iOS and Safari. |
||||
*/ |
||||
|
||||
button, |
||||
html [type="button"], /* 1 */ |
||||
[type="reset"], |
||||
[type="submit"] { |
||||
-webkit-appearance: button; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* Remove the inner border and padding in Firefox. |
||||
*/ |
||||
|
||||
button::-moz-focus-inner, |
||||
[type="button"]::-moz-focus-inner, |
||||
[type="reset"]::-moz-focus-inner, |
||||
[type="submit"]::-moz-focus-inner { |
||||
border-style: none; |
||||
padding: 0; |
||||
} |
||||
|
||||
/** |
||||
* Restore the focus styles unset by the previous rule. |
||||
*/ |
||||
|
||||
button:-moz-focusring, |
||||
[type="button"]:-moz-focusring, |
||||
[type="reset"]:-moz-focusring, |
||||
[type="submit"]:-moz-focusring { |
||||
outline: 1px dotted ButtonText; |
||||
} |
||||
|
||||
/** |
||||
* Correct the padding in Firefox. |
||||
*/ |
||||
|
||||
fieldset { |
||||
padding: 0.35em 0.75em 0.625em; |
||||
} |
||||
|
||||
/** |
||||
* 1. Correct the text wrapping in Edge and IE. |
||||
* 2. Correct the color inheritance from `fieldset` elements in IE. |
||||
* 3. Remove the padding so developers are not caught out when they zero out |
||||
* `fieldset` elements in all browsers. |
||||
*/ |
||||
|
||||
legend { |
||||
box-sizing: border-box; /* 1 */ |
||||
color: inherit; /* 2 */ |
||||
display: table; /* 1 */ |
||||
max-width: 100%; /* 1 */ |
||||
padding: 0; /* 3 */ |
||||
white-space: normal; /* 1 */ |
||||
} |
||||
|
||||
/** |
||||
* 1. Add the correct display in IE 9-. |
||||
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. |
||||
*/ |
||||
|
||||
progress { |
||||
display: inline-block; /* 1 */ |
||||
vertical-align: baseline; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* Remove the default vertical scrollbar in IE. |
||||
*/ |
||||
|
||||
textarea { |
||||
overflow: auto; |
||||
} |
||||
|
||||
/** |
||||
* 1. Add the correct box sizing in IE 10-. |
||||
* 2. Remove the padding in IE 10-. |
||||
*/ |
||||
|
||||
[type="checkbox"], |
||||
[type="radio"] { |
||||
box-sizing: border-box; /* 1 */ |
||||
padding: 0; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* Correct the cursor style of increment and decrement buttons in Chrome. |
||||
*/ |
||||
|
||||
[type="number"]::-webkit-inner-spin-button, |
||||
[type="number"]::-webkit-outer-spin-button { |
||||
height: auto; |
||||
} |
||||
|
||||
/** |
||||
* 1. Correct the odd appearance in Chrome and Safari. |
||||
* 2. Correct the outline style in Safari. |
||||
*/ |
||||
|
||||
[type="search"] { |
||||
-webkit-appearance: textfield; /* 1 */ |
||||
outline-offset: -2px; /* 2 */ |
||||
} |
||||
|
||||
/** |
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS. |
||||
*/ |
||||
|
||||
[type="search"]::-webkit-search-cancel-button, |
||||
[type="search"]::-webkit-search-decoration { |
||||
-webkit-appearance: none; |
||||
} |
||||
|
||||
/** |
||||
* 1. Correct the inability to style clickable types in iOS and Safari. |
||||
* 2. Change font properties to `inherit` in Safari. |
||||
*/ |
||||
|
||||
::-webkit-file-upload-button { |
||||
-webkit-appearance: button; /* 1 */ |
||||
font: inherit; /* 2 */ |
||||
} |
||||
|
||||
/* Interactive |
||||
========================================================================== */ |
||||
|
||||
/* |
||||
* Add the correct display in IE 9-. |
||||
* 1. Add the correct display in Edge, IE, and Firefox. |
||||
*/ |
||||
|
||||
details, /* 1 */ |
||||
menu { |
||||
display: block; |
||||
} |
||||
|
||||
/* |
||||
* Add the correct display in all browsers. |
||||
*/ |
||||
|
||||
summary { |
||||
display: list-item; |
||||
} |
||||
|
||||
/* Scripting |
||||
========================================================================== */ |
||||
|
||||
/** |
||||
* Add the correct display in IE 9-. |
||||
*/ |
||||
|
||||
canvas { |
||||
display: inline-block; |
||||
} |
||||
|
||||
/** |
||||
* Add the correct display in IE. |
||||
*/ |
||||
|
||||
template { |
||||
display: none; |
||||
} |
||||
|
||||
/* Hidden |
||||
========================================================================== */ |
||||
|
||||
/** |
||||
* Add the correct display in IE 10-. |
||||
*/ |
||||
|
||||
[hidden] { |
||||
display: none; |
||||
} |
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
.container { |
||||
|
||||
@media screen and (min-width: 768px) { |
||||
width: 100%; |
||||
max-width: 1630px; |
||||
} |
||||
} |
||||
|
||||
.container__wrap { |
||||
padding-left: 0; |
||||
padding-top: 90px; |
||||
min-height: 100vh; |
||||
transition: padding-left 0.3s; |
||||
|
||||
@include themify($themes) { |
||||
background: themed('colorBackgroundBody'); |
||||
} |
||||
|
||||
@media screen and (min-width: 576px) { |
||||
padding-left: 250px; |
||||
} |
||||
} |
||||
|
||||
.layout { |
||||
|
||||
&.layout--collapse { |
||||
|
||||
& + .container__wrap { |
||||
padding-left: 0; |
||||
|
||||
@media screen and (min-width: 576px) { |
||||
padding-left: 60px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
&.layout--top-navigation{ |
||||
|
||||
& + .container__wrap{ |
||||
|
||||
@media screen and (min-width: 576px) { |
||||
padding-left: 0; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
$themes: ( |
||||
light: ( |
||||
colorBackground: white, |
||||
colorBackgroundBody: #f2f4f7, |
||||
colorText: #646777, |
||||
colorTextAdditional: #646777, |
||||
logoImg: url(../shared/img/logo/logo_light.svg), |
||||
colorHover: #fafbfe, |
||||
colorBorder: #eff1f5, |
||||
colorIcon: #dddddd, |
||||
imgInvert: invert(0%), |
||||
colorFieldsBorder: #f2f4f7, |
||||
colorBubble: rgba(242, 244, 247, 0.65), |
||||
colorBubbleActive: rgba(234, 238, 255, 0.6), |
||||
colorScrollbar: #B4BFD0, |
||||
colorFitness: #646777, |
||||
), |
||||
dark: ( |
||||
colorBackground: #232329, |
||||
colorBackgroundBody: #2a2a31, |
||||
colorText: #dddddd, |
||||
colorTextAdditional: #999999, |
||||
logoImg: url(../shared/img/logo/logo_dark.svg), |
||||
colorHover: #38373f, |
||||
colorBorder: #333246, |
||||
colorIcon: #605f7b, |
||||
imgInvert: invert(100%), |
||||
colorFieldsBorder: #33333a, |
||||
colorBubble: rgba(68, 79, 97, 0.65), |
||||
colorBubbleActive: rgba(92, 104, 156, 0.6), |
||||
colorScrollbar: #606071, |
||||
colorFitness: #ffffff |
||||
) |
||||
); |
||||
|
||||
@mixin themify($themes) { |
||||
@each $theme, $map in $themes { |
||||
.theme-#{$theme} & { |
||||
$theme-map: () !global; |
||||
@each $key, $submap in $map { |
||||
$value: map-get(map-get($themes, $theme), '#{$key}'); |
||||
$theme-map: map-merge($theme-map, ($key: $value)) !global; |
||||
} |
||||
@content; |
||||
$theme-map: null !global; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@function themed($key) { |
||||
@return map-get($theme-map, $key); |
||||
} |
||||
|
||||
$color-accent: #4ce1b6; |
||||
$color-accent-hover: darken($color-accent, 10%); |
||||
$color-additional: #999999; |
||||
$color-additional-hover: darken($color-additional, 10%); |
||||
|
||||
$color-yellow: #f6da6e; |
||||
$color-yellow-hover: darken($color-yellow, 10%); |
||||
|
||||
$color-violet: #c88ffa; |
||||
|
||||
$color-red: #ff4861; |
||||
$color-red-hover: darken($color-red, 10%); |
||||
|
||||
$color-blue: #70bbfd; |
||||
$color-blue-hover: darken($color-blue, 10%); |
||||
|
||||
$color-gray: #787985; |
@ -0,0 +1,127 @@
@@ -0,0 +1,127 @@
|
||||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isLocalhost = Boolean( |
||||
window.location.hostname === 'localhost' || |
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' || |
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match( |
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ |
||||
) |
||||
); |
||||
|
||||
export function register(config) { |
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { |
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location); |
||||
if (publicUrl.origin !== window.location.origin) { |
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return; |
||||
} |
||||
|
||||
window.addEventListener('load', () => { |
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; |
||||
|
||||
if (isLocalhost) { |
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config); |
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => { |
||||
console.log( |
||||
'This web app is being served cache-first by a service ' + |
||||
'worker. To learn more, visit https://goo.gl/SC7cgQ' |
||||
); |
||||
}); |
||||
} else { |
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl, config); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
function registerValidSW(swUrl, config) { |
||||
navigator.serviceWorker |
||||
.register(swUrl) |
||||
.then(registration => { |
||||
registration.onupdatefound = () => { |
||||
const installingWorker = registration.installing; |
||||
installingWorker.onstatechange = () => { |
||||
if (installingWorker.state === 'installed') { |
||||
if (navigator.serviceWorker.controller) { |
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log('New content is available; please refresh.'); |
||||
|
||||
// Execute callback
|
||||
if (config.onUpdate) { |
||||
config.onUpdate(registration); |
||||
} |
||||
} else { |
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.'); |
||||
|
||||
// Execute callback
|
||||
if (config.onSuccess) { |
||||
config.onSuccess(registration); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
}; |
||||
}) |
||||
.catch(error => { |
||||
console.error('Error during service worker registration:', error); |
||||
}); |
||||
} |
||||
|
||||
function checkValidServiceWorker(swUrl, config) { |
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl) |
||||
.then(response => { |
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if ( |
||||
response.status === 404 || |
||||
response.headers.get('content-type').indexOf('javascript') === -1 |
||||
) { |
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => { |
||||
registration.unregister().then(() => { |
||||
window.location.reload(); |
||||
}); |
||||
}); |
||||
} else { |
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config); |
||||
} |
||||
}) |
||||
.catch(() => { |
||||
console.log( |
||||
'No internet connection found. App is running in offline mode.' |
||||
); |
||||
}); |
||||
} |
||||
|
||||
export function unregister() { |
||||
if ('serviceWorker' in navigator) { |
||||
navigator.serviceWorker.ready.then(registration => { |
||||
registration.unregister(); |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,121 @@
@@ -0,0 +1,121 @@
|
||||
/* eslint-disable jsx-a11y/label-has-for */ |
||||
import React, { PureComponent } from 'react'; |
||||
import CheckIcon from 'mdi-react/CheckIcon'; |
||||
import CloseIcon from 'mdi-react/CloseIcon'; |
||||
import PropTypes from 'prop-types'; |
||||
import classNames from 'classnames'; |
||||
|
||||
|
||||
class CheckBoxField extends PureComponent { |
||||
static propTypes = { |
||||
onChange: PropTypes.func.isRequired, |
||||
name: PropTypes.string.isRequired, |
||||
value: PropTypes.oneOfType([ |
||||
PropTypes.string, |
||||
PropTypes.bool, |
||||
]).isRequired, |
||||
label: PropTypes.string, |
||||
defaultChecked: PropTypes.bool, |
||||
disabled: PropTypes.bool, |
||||
className: PropTypes.string, |
||||
color: PropTypes.string, |
||||
}; |
||||
|
||||
static defaultProps = { |
||||
label: '', |
||||
defaultChecked: false, |
||||
disabled: false, |
||||
className: '', |
||||
color: '', |
||||
}; |
||||
|
||||
componentDidMount() { |
||||
const { onChange, defaultChecked } = this.props; |
||||
onChange(defaultChecked); |
||||
} |
||||
|
||||
render() { |
||||
const { |
||||
disabled, className, name, value, onChange, label, color, |
||||
} = this.props; |
||||
const CheckboxClass = classNames({ |
||||
'checkbox-btn': true, |
||||
disabled, |
||||
}); |
||||
|
||||
return ( |
||||
<label |
||||
className={`${CheckboxClass} ${className ? ` checkbox-btn--${className}` : ''}`} |
||||
htmlFor={name} |
||||
> |
||||
<input |
||||
className="checkbox-btn__checkbox" |
||||
type="checkbox" |
||||
id={name} |
||||
name={name} |
||||
onChange={onChange} |
||||
checked={value} |
||||
disabled={disabled} |
||||
/> |
||||
<span |
||||
className="checkbox-btn__checkbox-custom" |
||||
style={color ? { background: color, borderColor: color } : {}} |
||||
> |
||||
<CheckIcon /> |
||||
</span> |
||||
{className === 'button' |
||||
? ( |
||||
<span className="checkbox-btn__label-svg"> |
||||
<CheckIcon className="checkbox-btn__label-check" /> |
||||
<CloseIcon className="checkbox-btn__label-uncheck" /> |
||||
</span> |
||||
) : ''} |
||||
<span className="checkbox-btn__label"> |
||||
{label} |
||||
</span> |
||||
</label> |
||||
); |
||||
} |
||||
} |
||||
|
||||
const renderCheckBoxField = (props) => { |
||||
const { |
||||
input, label, defaultChecked, disabled, className, color, |
||||
} = props; |
||||
return ( |
||||
<CheckBoxField |
||||
{...input} |
||||
label={label} |
||||
defaultChecked={defaultChecked} |
||||
disabled={disabled} |
||||
className={className} |
||||
color={color} |
||||
/> |
||||
); |
||||
}; |
||||
|
||||
renderCheckBoxField.propTypes = { |
||||
input: PropTypes.shape({ |
||||
onChange: PropTypes.func, |
||||
name: PropTypes.string, |
||||
value: PropTypes.oneOfType([ |
||||
PropTypes.string, |
||||
PropTypes.bool, |
||||
]), |
||||
}).isRequired, |
||||
label: PropTypes.string, |
||||
defaultChecked: PropTypes.bool, |
||||
disabled: PropTypes.bool, |
||||
className: PropTypes.string, |
||||
color: PropTypes.string, |
||||
}; |
||||
|
||||
renderCheckBoxField.defaultProps = { |
||||
label: '', |
||||
defaultChecked: false, |
||||
disabled: false, |
||||
className: '', |
||||
color: '', |
||||
}; |
||||
|
||||
export default renderCheckBoxField; |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
import PropTypes from 'prop-types'; |
||||
|
||||
const { |
||||
string, shape, |
||||
} = PropTypes; |
||||
|
||||
export const SidebarProps = shape({ |
||||
show: PropTypes.bool, |
||||
collapse: PropTypes.bool, |
||||
}); |
||||
|
||||
export const ThemeProps = shape({ |
||||
className: string, |
||||
}); |