diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 362a42a..0000000 --- a/.babelrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "presets": ["@babel/env", "@babel/react"], - "plugins": [ - [ - "import", - { - "libraryName": "antd", - "libraryDirectory": "lib", - "camel2DashComponentName": true, - "style": "css" - } - ] - ] -} diff --git a/.eslintrc.js b/.eslintrc.js index 336e889..20630f3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,19 +3,17 @@ module.exports = { browser: true, es6: true, }, - extends: [ - 'plugin:react/recommended', - 'airbnb', - ], + extends: ['plugin:react/recommended', 'airbnb', 'react-app'], settings: { - "import/resolver": { + 'import/resolver': { node: { - extensions: ['.js', '.jsx', '.scss', '.css'], - moduleDirectory: ['node_modules', 'src/'], + extensions: ['.js', '.jsx', '.scss', '.css'], + moduleDirectory: ['node_modules', 'src/'], }, }, }, globals: { + __PATH_PREFIX__: true, Atomics: 'readonly', SharedArrayBuffer: 'readonly', }, @@ -26,9 +24,11 @@ module.exports = { ecmaVersion: 2018, sourceType: 'module', }, - plugins: [ - 'react', - ], + plugins: ['react'], rules: { + 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }], + 'react/prop-types': [0], + 'react/jsx-props-no-spreading': [0], + 'object-curly-newline': 'off', }, }; diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index f146ece..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -*.css linguist-detectable=false -*.js linguist-detectable=true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..b5c68e5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,7 +4,6 @@ about: Create a report to help us improve title: '' labels: '' assignees: '' - --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..2f28cea 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,7 +4,6 @@ about: Suggest an idea for this project title: '' labels: '' assignees: '' - --- **Is your feature request related to a problem? Please describe.** diff --git a/.gitignore b/.gitignore index 440a183..211935d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,74 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# dotenv environment variable files +.env* + +# gatsby files +.cache/ +public + +# Mac files +.DS_Store + +# Yarn +yarn-error.log +.pnp/ +.pnp.js +# Yarn Integrity file +.yarn-integrity + +# app level build/ .gitignore coverage/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..58d06c3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +.cache +package.json +package-lock.json +public diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a20502b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/.todo b/.todo new file mode 100644 index 0000000..df69f05 --- /dev/null +++ b/.todo @@ -0,0 +1,33 @@ + +✔ add pwa support @done +✔ darkmode @done +✔ animation @done +✔ save settings @done +✘ shortcuts @cancelled +✘ loop @cancelled +✔ clip image @done +✔ save blobs @done +✔ view saved blobs @done +✔ url id @done +✘ reset settigs @cancelled +✔ click logo to reoad to home @done +✔ complexity change no animation @done +✔ Sound from Zapsplat.com @done +✔ resize images @done +✔ add favicon @done +✔ update seo @done +✔ default image @done +✔ image clip fix @done + + +---- + +Features: + ✔ Dark theme @done + ✔ Faster initial load @done + ✔ Image clipping @done + ✔ Patterns view @done + ✔ Save and manage blobs locally @done + ✔ tiny sound @done + ✔ offline support @done + ✔ static url for sharing @done \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 349dc37..f1f4ba2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ Run unit tests ``` # run test cases - npm run test + npm run test # watch mode @@ -23,15 +23,19 @@ Run unit tests ``` ### Linting + ``` npm run lint ``` + It's also a good idea to add an eslint plugin in your editor. To fix lint errors from the command line: + ``` npm run lint ``` ## Submitting Pull Requests + This project follows [GitHub's standard forking model.](https://guides.github.com/activities/forking/). Please fork the project to submit pull requests. diff --git a/LICENSE b/LICENSE index c5f4709..b624a7c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 udaypydi +Copyright (c) 2021 Lokesh Rajendran Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 7b3c4de..6553b8b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

-Blobs

+Blobs

@@ -18,28 +18,24 @@ -### Preview - -![preview](https://user-images.githubusercontent.com/1754676/84493278-20f9d480-acc5-11ea-8667-ee0df8f35442.png) - -![preview2](https://user-images.githubusercontent.com/1754676/84492711-3a4e5100-acc4-11ea-942c-37f9cd802fcf.png) - -### Blobs for flutter - -  - -
-
-
Blobs for flutter
-
flutter_blobs
-
-
- -

- style_custom_color - style_gradient - blobs_with_child -

+

+Social preview

+ + + +### Features + - Choose any solid colors + - Set gradient colors + - Use Patterns + - Clip Image + - Outlined blob + - SVG Code - view/copy/download + - Flutter blob code + - Save and view blobs + - Tiny blob change sound + - PWA - Offline support + - Custom sharable URL + - Dark theme ### Development diff --git a/config/assetsMock.js b/config/assetsMock.js deleted file mode 100644 index 3cdf9be..0000000 --- a/config/assetsMock.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ''; diff --git a/config/paths.js b/config/paths.js deleted file mode 100644 index 06a8c31..0000000 --- a/config/paths.js +++ /dev/null @@ -1,11 +0,0 @@ -const path = require("path"); - -const paths = { - entry: path.resolve(__dirname, "../index"), - output: path.resolve(__dirname, "../build"), - assets: path.resolve(__dirname, "../src/assets"), - template: path.resolve(__dirname, "./template.html"), - components: path.resolve(__dirname, "../src/components"), -}; - -module.exports = paths; diff --git a/config/template.html b/config/template.html deleted file mode 100644 index 404e0ff..0000000 --- a/config/template.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - Blobs - Generate beautiful blob shapes for web and flutter apps - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - diff --git a/config/webpack.base.config.js b/config/webpack.base.config.js deleted file mode 100644 index 162c44d..0000000 --- a/config/webpack.base.config.js +++ /dev/null @@ -1,77 +0,0 @@ -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); -const CopyPlugin = require("copy-webpack-plugin"); -const paths = require("./paths"); - -module.exports = function ({ plugins }) { - return { - entry: { - index: paths.entry, - }, - output: { - path: paths.output, - filename: "[name].bundle.js", - }, - module: { - rules: [ - { - test: /(.js|.jsx)/, - exclude: /(node_modules|bower_components)/, - use: "babel-loader", - }, - { - test: /\.s[ac]ss$/i, - use: ["style-loader", "css-loader", "sass-loader"], - }, - { - test: /\.css$/i, - use: ["style-loader", "css-loader", "postcss-loader"], - }, - { - test: /\.(png|jpe?g|gif)$/i, - use: [ - { - loader: "file-loader", - }, - ], - }, - { - test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, - use: [ - { - loader: "file-loader", - options: { - name: "[name].[ext]", - }, - }, - ], - }, - ], - }, - plugins: [ - ...plugins, - new HtmlWebpackPlugin({ - template: paths.template, - hash: true, - title: "Blobs for web and flutter", - }), - new MiniCssExtractPlugin({ - filename: "[name].[hash].css", - chunkFilename: "[id].[hash].css", - }), - new CopyPlugin({ - patterns: [ - { from: "src/assets/favicons", to: "favicons" }, - { from: "src/assets/posters", to: "posters" }, - ], - }), - ], - resolve: { - extensions: [".js", ".jsx", ".scss", ".css"], - alias: { - components: paths.components, - assets: paths.assets, - }, - }, - }; -}; diff --git a/config/webpack.dev.config.js b/config/webpack.dev.config.js deleted file mode 100644 index c566028..0000000 --- a/config/webpack.dev.config.js +++ /dev/null @@ -1,14 +0,0 @@ -const webpackBaseConfig = require("./webpack.base.config"); -const paths = require("./paths"); - -module.exports = { - mode: "development", - devtool: "eval-source-map", - ...webpackBaseConfig({ plugins: [] }), - devServer: { - contentBase: paths.output, - port: 9000, - host: "0.0.0.0", - hot: true, - }, -}; diff --git a/config/webpack.prod.config.js b/config/webpack.prod.config.js deleted file mode 100644 index ac852bc..0000000 --- a/config/webpack.prod.config.js +++ /dev/null @@ -1,17 +0,0 @@ -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); -const webpackBaseConfig = require('./webpack.base.config'); - -module.exports = { - mode: 'production', - devtool: 'cheap-source-map', - ...webpackBaseConfig({ - plugins: [ - new CleanWebpackPlugin(), - new BundleAnalyzerPlugin({ - analyzerMode: 'static', - defaultSizes: 'gzip' - }), - ] - }), -}; diff --git a/gatsby-browser.js b/gatsby-browser.js new file mode 100644 index 0000000..e69de29 diff --git a/gatsby-config.js b/gatsby-config.js new file mode 100644 index 0000000..e60147f --- /dev/null +++ b/gatsby-config.js @@ -0,0 +1,57 @@ +module.exports = { + siteMetadata: { + siteUrl: 'https://blobs.app', + name: 'blobs.app', + description: 'Generate beautiful blob shapesfor web and flutter apps', + descriptionLong: + 'Customizable blobs as SVG and Flutter Widget. Create random or fixed blobs, loop, animate, clip them with ease', + short_name: 'Blob generator', + start_url: '/', + background_color: '#d7819b', + theme_color: '#d7819b', + display: 'minimal-ui', + icon: 'src/images/blobs.png', + author: '@lokesh_coder', + }, + plugins: [ + 'gatsby-plugin-react-helmet', + 'gatsby-plugin-image', + { + resolve: 'gatsby-source-filesystem', + options: { + name: 'images', + path: `${__dirname}/src/images`, + }, + }, + 'gatsby-transformer-sharp', + 'gatsby-plugin-sharp', + { + resolve: 'gatsby-plugin-manifest', + options: { + name: 'blobs.app', + short_name: 'Blob generator', + start_url: '/', + background_color: '#ffffff', + theme_color: '#d7819b', + display: 'standalone', + icon: 'src/images/blobs.png', + }, + }, + + { + resolve: 'gatsby-plugin-google-analytics', + options: { + trackingId: 'UA-167428362-1', + }, + }, + { + resolve: '@chakra-ui/gatsby-plugin', + options: { + isResettingCSS: true, + isUsingColorMode: true, + portalZIndex: 40, + }, + }, + '@chakra-ui/gatsby-plugin', + ], +}; diff --git a/gatsby-node.js b/gatsby-node.js new file mode 100644 index 0000000..e69de29 diff --git a/gatsby-ssr.js b/gatsby-ssr.js new file mode 100644 index 0000000..e69de29 diff --git a/index.js b/index.js deleted file mode 100644 index aa58a40..0000000 --- a/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import React, { Suspense } from "react"; -import ReactDOM from "react-dom"; -import "assets/css/main.css"; -import "./src/app.scss"; -import Loader from "./src/components/loader/loader.component"; - -const App = React.lazy(() => import("./src/app")); - -const Index = () => ( - }> - - -); - -ReactDOM.render(, document.getElementById("root")); diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index ed77a9a..0000000 --- a/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - moduleDirectories: [ - 'node_modules', - __dirname - ], - clearMocks: true, - coverageDirectory: "coverage", - moduleNameMapper: { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/config/assetsMock.js", - "\\.(css|less|scss)$": "identity-obj-proxy", - "^uielements(.*)$": "/src/uielements$1" - }, - testEnvironment: "jsdom", -}; diff --git a/package.json b/package.json index 42e537d..d2b15b0 100644 --- a/package.json +++ b/package.json @@ -1,66 +1,79 @@ { "name": "blobs.app", - "version": "1.1.0", - "description": "Generate blob shapes for Web and Flutter", - "main": "index.js", - "scripts": { - "build": "rm -rf build && NODE_ENV=production npm run build:css && webpack ---config ./config/webpack.prod.config.js", - "dev": "npm run watch:css && webpack-dev-server --config ./config/webpack.prod.config.js --open", - "lint": "./node_modules/.bin/eslint --ext .js,.jsx src/", - "lint: fix": "./node_modules/.bin/eslint --ext .js,.jsx src/ --fix", - "build:css": "postcss src/assets/css/styles.css -o src/assets/css/main.css", - "watch:css": "postcss src/assets/css/styles.css -o src/assets/css/main.css", - "test": "jest --collect-coverage", - "test:watch": "jest --watch" + "private": true, + "description": "Blobs - Generate beautiful blob shapes for web and flutter apps", + "version": "2.0.0", + "author": "Lokesh Rajendran ", + "dependencies": { + "@chakra-ui/gatsby-plugin": "^1.0.1", + "@chakra-ui/react": "^1.3.4", + "@emotion/react": "^11.1.5", + "@emotion/styled": "^11.1.5", + "framer-motion": "^3.10.3", + "gatsby": "^3.0.1", + "gatsby-plugin-gatsby-cloud": "^2.0.0", + "gatsby-plugin-image": "^1.0.0", + "gatsby-plugin-manifest": "^3.0.0", + "gatsby-plugin-offline": "^4.0.0", + "gatsby-plugin-react-helmet": "^4.0.0", + "gatsby-plugin-sharp": "^3.0.0", + "gatsby-source-filesystem": "^3.0.0", + "gatsby-transformer-sharp": "^3.0.0", + "prop-types": "^15.7.2", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "react-helmet": "^6.1.0", + "@chakra-ui/icons": "^1.0.5", + "blobshape": "^1.0.0", + "dynamics.js": "^1.1.5", + "gatsby-image": "^3.0.0", + "gatsby-plugin-google-analytics": "^3.0.0", + "gatsby-plugin-sitemap": "^3.0.0", + "gatsby-rehype-prismjs": "^2.0.0", + "gatsby-remark-prismjs": "^4.0.0", + "gatsby-transformer-rehype": "^2.0.0", + "gatsby-transformer-remark": "^3.0.0", + "prismjs": "^1.23.0", + "project-name-generator": "^2.1.9", + "react-syntax-highlighter": "^15.4.3", + "redux-zero": "^5.1.7", + "use-sound": "^2.0.1" + }, + "devDependencies": { + "babel-eslint": "^10.1.0", + "eslint": "^7.21.0", + "eslint-config-airbnb": "^18.2.1", + "eslint-config-react-app": "^6.0.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-react": "^7.22.0", + "eslint-plugin-react-hooks": "^4.2.0", + "prettier": "2.2.1" }, "keywords": [ - "blobs" + "blobs", + "generate", + "render", + "shapes", + "create" ], - "author": "Lokesh Rajendran", "license": "MIT", - "dependencies": { - "@babel/core": "^7.9.0", - "@babel/preset-env": "^7.9.0", - "@babel/preset-react": "^7.9.4", - "@risingstack/react-easy-state": "^6.3.0", - "antd": "^4.2.4", - "autoprefixer": "^9.7.6", - "babel-loader": "^8.1.0", - "dynamics.js": "^1.1.5", - "postcss-cli": "^7.1.0", - "prop-types": "^15.7.2", - "react": "^16.13.1", - "react-dom": "^16.13.1", - "react-syntax-highlighter": "^12.2.1", - "tailwindcss": "^1.4.6", - "webpack": "^4.42.1", - "webpack-cli": "^3.3.11" + "scripts": { + "build": "gatsby build", + "develop": "gatsby develop -H 0.0.0.0", + "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"", + "start": "npm run develop", + "serve": "gatsby serve", + "clean": "gatsby clean", + "lint": "./node_modules/.bin/eslint --ext .js,.jsx src/", + "lint:fix": "./node_modules/.bin/eslint --ext .js,.jsx src/ --fix", + "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1" }, - "devDependencies": { - "@fullhuman/postcss-purgecss": "^2.2.0", - "@testing-library/jest-dom": "^5.5.0", - "@testing-library/react": "^10.0.2", - "babel-plugin-import": "^1.13.0", - "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^6.0.1", - "css-loader": "^3.5.2", - "cssnano": "^4.1.10", - "eslint": "^6.8.0", - "eslint-config-airbnb": "^18.1.0", - "eslint-plugin-import": "^2.20.2", - "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-react": "^7.19.0", - "eslint-plugin-react-hooks": "^2.5.1", - "file-loader": "^6.0.0", - "html-webpack-plugin": "^4.0.4", - "identity-obj-proxy": "^3.0.0", - "jest": "^25.3.0", - "mini-css-extract-plugin": "^0.9.0", - "node-sass": "^4.13.1", - "postcss-loader": "^3.0.0", - "sass-loader": "^8.0.2", - "style-loader": "^1.1.3", - "webpack-bundle-analyzer": "^3.6.1", - "webpack-dev-server": "^3.10.3" + "repository": { + "type": "git", + "url": "https://github.com/lokesh-coder/blobs.app" + }, + "bugs": { + "url": "https://github.com/lokesh-coder/blobs.app/issues" } } diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 9c2c0d2..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,10 +0,0 @@ -const tailwindcss = require("tailwindcss"); -module.exports = { - plugins: [ - tailwindcss("./tailwind.config.js"), - require("autoprefixer"), - require("cssnano")({ - preset: "default", - }), - ], -}; diff --git a/src/@chakra-ui/gatsby-plugin/theme.js b/src/@chakra-ui/gatsby-plugin/theme.js new file mode 100644 index 0000000..4d92cad --- /dev/null +++ b/src/@chakra-ui/gatsby-plugin/theme.js @@ -0,0 +1,127 @@ +import { extendTheme } from '@chakra-ui/react'; + +const theme = extendTheme({ + colors: { + primary: '#d7819b', + dark: { + heaading: '#1d3557', + }, + }, + styles: { + global: ({ colorMode }) => ({ + body: { + bg: colorMode === 'dark' ? 'blue.800' : 'white', + }, + '.chakra-modal__content-container': { + p: { base: '2', lg: '0' }, + }, + '#blobSvg': { + overflow: 'visible', + }, + }), + }, + components: { + Heading: { + baseStyle: { + letterSpacing: '-1px', + }, + variants: { + main: ({ colorMode }) => ({ + fontWeight: '900', + color: colorMode === 'dark' ? 'gray.300' : '#1d3557', + }), + }, + }, + Text: { + baseStyle: { + color: 'gray.500', + }, + variants: { + subtle: ({ colorMode }) => ({ + color: colorMode === 'dark' ? 'gray.300' : 'gray.700', + }), + }, + }, + Button: { + variants: { + subtle: ({ colorMode }) => ({ + color: colorMode === 'dark' ? 'gray.400' : 'gray.500', + fontWeight: 400, + fontSize: 'sm', + lineHeight: 'initial', + _hover: { bg: colorMode === 'dark' ? 'gray.800' : 'gray.100' }, + }), + main: ({ colorMode }) => ({ + bg: 'gray.500', + fontWeight: '600', + color: 'white', + w: 'full', + p: '7', + fontSize: 'md', + lineHeight: 'initial', + transition: 'all 0.4s ease', + _hover: { bg: colorMode === 'dark' ? '#d7819b' : 'gray.600' }, + _focus: { + bg: colorMode === 'dark' ? '#d7819b' : 'gray.700', + shadow: 'xl', + outline: 'none', + }, + _active: { + transform: 'scale(0.95)', + bg: colorMode === 'dark' ? '#d7819b' : 'gray.500', + }, + }), + + heavy: ({ colorMode }) => ({ + bg: 'transparent', + color: colorMode === 'dark' ? 'gray.400' : 'gray.500', + fontWeight: 700, + fontSize: '13px', + borderWidth: '1px', + borderColor: 'transparent', + rounded: 'md', + textTransform: 'uppercase', + lineHeight: 'initial', + _hover: { + bg: colorMode === 'dark' ? 'gray.700' : 'gray.600', + color: '#fff', + textDecoration: 'none !important', + borderColor: colorMode === 'dark' ? 'gray.600' : 'gray.600', + shadow: 'xl', + }, + _focus: { boxShadow: 'none' }, + }), + silent: ({ colorMode }) => ({ + fontWeight: 'normal', + fontSize: 'sm', + color: colorMode === 'dark' ? 'gray.400' : 'gray.500', + outline: 'none', + lineHeight: 'initial', + _hover: { + color: colorMode === 'dark' ? 'gray.200' : 'gray.700', + textDecoration: 'none', + }, + _focus: { + outline: 'none', + boxShadow: 'none', + color: colorMode === 'dark' ? 'gray.200' : 'gray.700', + }, + }), + }, + }, + Tooltip: { + variants: { + default: () => ({ + color: 'gray.50', + bg: 'gray.500', + fontWeight: '400', + fontSize: 'sm', + rounded: 'sm', + px: '3', + py: '1', + }), + }, + }, + }, +}); +export default extendTheme(theme); diff --git a/src/app.jsx b/src/app.jsx deleted file mode 100644 index 252d27e..0000000 --- a/src/app.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import React, { useEffect } from "react"; -import { store, autoEffect } from "@risingstack/react-easy-state"; -import Grid from "./components/layout/grid"; -import { appStore } from "./store"; -import animator from "./services/animator"; - -autoEffect(() => { - animator(appStore.path); -}); - -export default function App() { - return ; -} diff --git a/src/app.scss b/src/app.scss deleted file mode 100644 index 5956f1e..0000000 --- a/src/app.scss +++ /dev/null @@ -1,117 +0,0 @@ -html body { - line-height: 25px; - font-family: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; - button { - line-height: 25px; - } - .brand-bg { - margin-top: -16px; - transform: translate(-50%, 0); - margin-left: 50%; - } - - #blobSvg { - max-width: 500px; - overflow: visible; - } - - .stroke { - border: 4px solid #d1d8e0; - &.stroke-fill { - position: relative; - display: flex; - align-items: center; - justify-content: center; - &:after { - content: ""; - - width: 100%; - height: 100%; - transform: scale(0.7); - border-radius: 100%; - background-color: #4a5568; - } - } - } - - .ant-slider-step { - background: rgb(209, 216, 224); - } - - .ant-slider-handle { - border: solid 3px #4e566b; - background-color: #f7fafc; - width: 24px; - height: 24px; - margin-top: -9px; - &:focus, - &.ant-tooltip-open { - border-color: #303749; - } - } - .ant-slider:hover .ant-slider-handle:not(.ant-tooltip-open) { - border-color: #303749; - } - - .ant-modal-mask { - background-color: rgba(78, 86, 107, 0.71); - } - - .ant-modal-content { - border-radius: 11px; - } - - .ant-tooltip-inner { - border-radius: 4px; - font-size: 11px; - color: #ffffffd6; - background: #2f3143; - } - - .ant-popover-inner { - max-width: 300px; - border-radius: 10px; - } - - .ant-popover-arrow { - display: none; - } - - .ant-popover-inner-content { - padding: 25px; - background: rgba(207, 214, 222, 0.15); - } - - input.ant-input { - border-radius: 6px; - padding: 9px; - font-weight: bold; - color: #828b99; - border-color: #cfd8df; - border-width: 2px; - } - - .ant-input:hover { - border-color: #4e566b; - border-right-width: 2px !important; - } - - .ant-input:focus, - .ant-input-focused { - border-color: #4c5468; - border-right-width: 2px !important; - outline: 0; - -webkit-box-shadow: 0 0 0 2px rgba(78, 86, 107, 0.34); - box-shadow: 0 0 0 2px rgba(78, 86, 107, 0.34); - } - - pre { - margin: 0 !important; - } - pre > code { - overflow: auto; - width: 100%; - display: inline-block; - } -} diff --git a/src/assets/blobs_logo.png b/src/assets/blobs_logo.png deleted file mode 100644 index 52bc10c..0000000 Binary files a/src/assets/blobs_logo.png and /dev/null differ diff --git a/src/assets/css/main.css b/src/assets/css/main.css deleted file mode 100644 index 4fdbe4b..0000000 --- a/src/assets/css/main.css +++ /dev/null @@ -1 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none;padding:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#a0aec0}input::-moz-placeholder,textarea::-moz-placeholder{color:#a0aec0}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#a0aec0}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:#a0aec0}input::placeholder,textarea::placeholder{color:#a0aec0}[role=button],button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}.bg-theme-400{--bg-opacity:1;background-color:#4f576c;background-color:rgba(79,87,108,var(--bg-opacity))}.hover\:bg-gray-300:hover{--bg-opacity:1;background-color:#e2e8f0;background-color:rgba(226,232,240,var(--bg-opacity))}.hover\:bg-theme-900:hover{--bg-opacity:1;background-color:#131728;background-color:rgba(19,23,40,var(--bg-opacity))}.rounded-md{border-radius:.375rem}.rounded-full{border-radius:9999px}.cursor-pointer{cursor:pointer}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.font-medium{font-weight:500}.font-extrabold{font-weight:800}.h-6{height:1.5rem}.h-8{height:2rem}.h-screen{height:100vh}.text-xs{font-size:.75rem}.text-sm{font-size:.875rem}.text-base{font-size:1rem}.text-lg{font-size:1.125rem}.text-2xl{font-size:1.5rem}.text-3xl{font-size:1.875rem}.leading-none{line-height:1}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-8{margin-top:2rem;margin-bottom:2rem}.mx-auto{margin-left:auto;margin-right:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mr-6{margin-right:1.5rem}.mb-6{margin-bottom:1.5rem}.max-w-screen-xl{max-width:1280px}.min-h-screen{min-height:100vh}.focus\:outline-none:focus{outline:0}.p-2{padding:.5rem}.p-6{padding:1.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.shadow-lg{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05)}.text-center{text-align:center}.text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-theme-300{--text-opacity:1;color:#6e7486;color:rgba(110,116,134,var(--text-opacity))}.text-theme-600{--text-opacity:1;color:#2b334b;color:rgba(43,51,75,var(--text-opacity))}.hover\:text-gray-600:hover{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.hover\:text-theme-800:hover{--text-opacity:1;color:#1e2438;color:rgba(30,36,56,var(--text-opacity))}.tracking-tight{letter-spacing:-.025em}.visible{visibility:visible}.w-2{width:.5rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.w-screen{width:100vw}.transform{--transform-translate-x:0;--transform-translate-y:0;--transform-rotate:0;--transform-skew-x:0;--transform-skew-y:0;--transform-scale-x:1;--transform-scale-y:1;transform:translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y))}.scale-150{--transform-scale-x:1.5;--transform-scale-y:1.5}@import "../icons/remixicon.css";@media (min-width:768px){.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-3\/12{width:25%}.md\:w-9\/12{width:75%}} \ No newline at end of file diff --git a/src/assets/css/styles.css b/src/assets/css/styles.css deleted file mode 100644 index aa8a9e1..0000000 --- a/src/assets/css/styles.css +++ /dev/null @@ -1,7 +0,0 @@ -@import "tailwindcss/base"; - -@import "tailwindcss/components"; - -@import "tailwindcss/utilities"; - -@import "../icons/remixicon.css"; diff --git a/src/assets/favicons/android-chrome-192x192.png b/src/assets/favicons/android-chrome-192x192.png deleted file mode 100644 index 03aeae2..0000000 Binary files a/src/assets/favicons/android-chrome-192x192.png and /dev/null differ diff --git a/src/assets/favicons/android-chrome-256x256.png b/src/assets/favicons/android-chrome-256x256.png deleted file mode 100644 index 8f96f20..0000000 Binary files a/src/assets/favicons/android-chrome-256x256.png and /dev/null differ diff --git a/src/assets/favicons/apple-touch-icon.png b/src/assets/favicons/apple-touch-icon.png deleted file mode 100644 index 3b2f676..0000000 Binary files a/src/assets/favicons/apple-touch-icon.png and /dev/null differ diff --git a/src/assets/favicons/browserconfig.xml b/src/assets/favicons/browserconfig.xml deleted file mode 100644 index 101f5a7..0000000 --- a/src/assets/favicons/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - #ffffff - - - diff --git a/src/assets/favicons/favicon-16x16.png b/src/assets/favicons/favicon-16x16.png deleted file mode 100644 index e493c4a..0000000 Binary files a/src/assets/favicons/favicon-16x16.png and /dev/null differ diff --git a/src/assets/favicons/favicon-32x32.png b/src/assets/favicons/favicon-32x32.png deleted file mode 100644 index 45eb996..0000000 Binary files a/src/assets/favicons/favicon-32x32.png and /dev/null differ diff --git a/src/assets/favicons/favicon.ico b/src/assets/favicons/favicon.ico deleted file mode 100644 index 8618b2f..0000000 Binary files a/src/assets/favicons/favicon.ico and /dev/null differ diff --git a/src/assets/favicons/mstile-150x150.png b/src/assets/favicons/mstile-150x150.png deleted file mode 100644 index 217cc95..0000000 Binary files a/src/assets/favicons/mstile-150x150.png and /dev/null differ diff --git a/src/assets/favicons/safari-pinned-tab.svg b/src/assets/favicons/safari-pinned-tab.svg deleted file mode 100644 index d72e5bb..0000000 --- a/src/assets/favicons/safari-pinned-tab.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/favicons/site.webmanifest b/src/assets/favicons/site.webmanifest deleted file mode 100644 index 2612717..0000000 --- a/src/assets/favicons/site.webmanifest +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "", - "short_name": "", - "icons": [ - { - "src": "/favicons/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/favicons/android-chrome-256x256.png", - "sizes": "256x256", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/src/assets/icons/remixicon.css b/src/assets/icons/remixicon.css deleted file mode 100644 index 3e2d43b..0000000 --- a/src/assets/icons/remixicon.css +++ /dev/null @@ -1,75 +0,0 @@ - -/* -* https://remixicon.com -* https://github.com/Remix-Design/RemixIcon -* Copyright RemixIcon.com -* Released under the Apache License Version 2.0 -*/ - -@font-face { - font-family: "remixicon"; - src: url('remixicon.eot?t=1590213533649'); /* IE9*/ - src: url('remixicon.eot?t=1590213533649#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url("remixicon.woff2?t=1590213533649") format("woff2"), - url("remixicon.woff?t=1590213533649") format("woff"), - url('remixicon.ttf?t=1590213533649') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ - url('remixicon.svg?t=1590213533649#remixicon') format('svg'); /* iOS 4.1- */ - font-display: swap; -} - -[class^="ri-"], [class*="ri-"] { - font-family: 'remixicon' !important; - font-style: normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.ri-lg { font-size: 1.3333em; line-height: 0.75em; vertical-align: -.0667em; } -.ri-xl { font-size: 1.5em; line-height: 0.6666em; vertical-align: -.075em; } -.ri-xxs { font-size: .5em; } -.ri-xs { font-size: .75em; } -.ri-sm { font-size: .875em } -.ri-1x { font-size: 1em; } -.ri-2x { font-size: 2em; } -.ri-3x { font-size: 3em; } -.ri-4x { font-size: 4em; } -.ri-5x { font-size: 5em; } -.ri-6x { font-size: 6em; } -.ri-7x { font-size: 7em; } -.ri-8x { font-size: 8em; } -.ri-9x { font-size: 9em; } -.ri-10x { font-size: 10em; } -.ri-fw { text-align: center; width: 1.25em; } - -.ri-question-line:before { content: "\efeb"; } -.ri-github-fill:before { content: "\eda3"; } -.ri-twitter-line:before { content: "\f1c9"; } -.ri-flutter-fill:before { content: "\ed20"; } -.ri-code-s-slash-fill:before { content: "\eba4"; } -.ri-code-fill:before { content: "\eba0"; } -.ri-download-line:before { content: "\ec3e"; } -.ri-download-2-line:before { content: "\ec38"; } -.ri-shuffle-fill:before { content: "\f0bd"; } -.ri-copyright-line:before { content: "\ebd6"; } -.ri-paint-brush-line:before { content: "\ef6d"; } -.ri-palette-line:before { content: "\ef71"; } -.ri-file-copy-2-line:before { content: "\ecb3"; } -.ri-file-copy-fill:before { content: "\ecb4"; } -.ri-file-list-2-line:before { content: "\eccd"; } -.ri-folder-2-line:before { content: "\ed2b"; } -.ri-folder-line:before { content: "\ed43"; } -.ri-exchange-line:before { content: "\ec8d"; } -.ri-repeat-2-line:before { content: "\f017"; } -.ri-apps-2-line:before { content: "\ea40"; } -.ri-alert-line:before { content: "\ea1f"; } -.ri-close-fill:before { content: "\eb92"; } -.ri-check-fill:before { content: "\eb74"; } -.ri-refresh-line:before { content: "\f009"; } -.ri-loader-2-fill:before { content: "\ee82"; } -.ri-flashlight-line:before { content: "\ed17"; } -.ri-plug-line:before { content: "\efc3"; } -.ri-lightbulb-flash-line:before { content: "\ee69"; } -.ri-heart-line:before { content: "\eddc"; } -.ri-heart-fill:before { content: "\eddb"; } -.ri-file-mark-line:before { content: "\ecd5"; } -.ri-pushpin-2-line:before { content: "\efdd"; } diff --git a/src/assets/icons/remixicon.eot b/src/assets/icons/remixicon.eot deleted file mode 100644 index 2d4bde6..0000000 Binary files a/src/assets/icons/remixicon.eot and /dev/null differ diff --git a/src/assets/icons/remixicon.svg b/src/assets/icons/remixicon.svg deleted file mode 100644 index 60f80c1..0000000 --- a/src/assets/icons/remixicon.svg +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/assets/icons/remixicon.symbol.svg b/src/assets/icons/remixicon.symbol.svg deleted file mode 100644 index d9443fe..0000000 --- a/src/assets/icons/remixicon.symbol.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/icons/remixicon.ttf b/src/assets/icons/remixicon.ttf deleted file mode 100644 index 66506be..0000000 Binary files a/src/assets/icons/remixicon.ttf and /dev/null differ diff --git a/src/assets/icons/remixicon.woff b/src/assets/icons/remixicon.woff deleted file mode 100644 index 0cafba7..0000000 Binary files a/src/assets/icons/remixicon.woff and /dev/null differ diff --git a/src/assets/icons/remixicon.woff2 b/src/assets/icons/remixicon.woff2 deleted file mode 100644 index 231fe29..0000000 Binary files a/src/assets/icons/remixicon.woff2 and /dev/null differ diff --git a/src/assets/poster.png b/src/assets/poster.png deleted file mode 100644 index d3eb229..0000000 Binary files a/src/assets/poster.png and /dev/null differ diff --git a/src/assets/posters/blobs_app_poster.png b/src/assets/posters/blobs_app_poster.png deleted file mode 100644 index 375cbf6..0000000 Binary files a/src/assets/posters/blobs_app_poster.png and /dev/null differ diff --git a/src/components/Blob.js b/src/components/Blob.js new file mode 100644 index 0000000..eeb3e55 --- /dev/null +++ b/src/components/Blob.js @@ -0,0 +1,120 @@ +/* eslint-disable import/no-absolute-path */ +/* eslint-disable import/no-unresolved */ +import { Flex, Spinner, useColorModeValue } from '@chakra-ui/react'; +import React, { useRef, useState } from 'react'; +import * as Patterns from '../patterns'; +import LoadingImg from '../images/loadingimg.png'; + +const Blob = ({ + size, + isOutline, + type, + svgPath, + color, + colors, + image, + pattern, +}) => { + const ref = useRef(null); + const [imgLoaded, setImgLoaded] = useState(false); + const pattenBgColor = useColorModeValue('#d1d8e0', '#6c7c93'); + + const props = { + fill: color, + }; + if (type === 'gradient') { + props.fill = 'url(#gradient)'; + } + if (isOutline) { + props.strokeWidth = '7px'; + props.fill = 'none'; + props.stroke = color; + } + if (type === 'gradient' && isOutline) { + props.stroke = 'url(#gradient)'; + } + if (!svgPath) { + return ( + + + + ); + } + + return ( + + {type === 'solid' && } + {type === 'gradient' && ( + <> + + + + + + + + + )} + {type === 'pattern' && ( + <> + + + + + + + + )} + + {type === 'image' && ( + <> + + + + + + {!imgLoaded && ( + + )} + { + setImgLoaded(true); + }} + /> + + )} + + ); +}; + +export default Blob; diff --git a/src/components/BlobActionBar.js b/src/components/BlobActionBar.js new file mode 100644 index 0000000..d613d3f --- /dev/null +++ b/src/components/BlobActionBar.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { Container, Box, HStack } from '@chakra-ui/react'; +import RandomizerBtn from './Controls/RandomizerButton'; +import HtmlCodeModalButton from './Controls/HtmlCodeModalButton'; +import { dynamic } from '../state'; +import FlutterCodeModalButton from './Controls/FlutterCodeModalButton'; + +const BlobActionBar = () => ( + + + + + + + + + +); + +export default dynamic(BlobActionBar); diff --git a/src/components/BlobContainer.js b/src/components/BlobContainer.js new file mode 100644 index 0000000..436ba7d --- /dev/null +++ b/src/components/BlobContainer.js @@ -0,0 +1,46 @@ +import React, { useEffect } from 'react'; +import { dynamic } from '../state'; +import { + createFixedBlob, + createInitialBlob, + setBlobTheme, +} from '../utils/blob.utils'; +import Blob from './Blob'; + +const BlobContainer = (props) => { + const { edges, growth, color, colors, type, isOutline, svgPath } = props; + + useEffect(() => { + createInitialBlob(props); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (svgPath) createFixedBlob(props); + }, [growth]); + + useEffect(() => { + if (svgPath) createFixedBlob(props, false); + }, [edges]); + + useEffect(() => { + if (svgPath) setBlobTheme(props); + }, [color, colors, isOutline, type]); + + return ; +}; + +export default dynamic(BlobContainer, [ + 'size', + 'color', + 'colors', + 'color', + 'type', + 'isOutline', + 'svgPath', + 'edges', + 'growth', + 'seed', + 'image', + 'pattern', +]); diff --git a/src/components/BlobSettingsSection.js b/src/components/BlobSettingsSection.js new file mode 100644 index 0000000..908c0b3 --- /dev/null +++ b/src/components/BlobSettingsSection.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { Box, Divider, HStack } from '@chakra-ui/react'; + +import RandomnessSlider from './Controls/RandomnessSlider'; +import ComplexitySlider from './Controls/ComplexitySlider'; +import SolidColorPicker from './Controls/SolidColorPicker'; +import GradientColorsPicker from './Controls/GradientColorsPicker'; +import OutlineToggleButton from './Controls/OutlineToggleButton'; +import ImageSetter from './Controls/ImageSetter'; +import PatternSetter from './Controls/PatternSetter'; + +const BlobSettingsSection = () => ( + + + + + + + + + + + + + +); + +export default BlobSettingsSection; diff --git a/src/components/Common/Ding.js b/src/components/Common/Ding.js new file mode 100644 index 0000000..4ffa989 --- /dev/null +++ b/src/components/Common/Ding.js @@ -0,0 +1,34 @@ +import { Button, Box, Flex, Text } from '@chakra-ui/react'; +import React from 'react'; + +const Ding = ({ isSelected, activeComp, label, Icon, ...props }) => ( + +); + +export default Ding; diff --git a/src/components/Common/DownloadSVG.js b/src/components/Common/DownloadSVG.js new file mode 100644 index 0000000..602338d --- /dev/null +++ b/src/components/Common/DownloadSVG.js @@ -0,0 +1,25 @@ +import { DownloadIcon } from '@chakra-ui/icons'; +import { Button } from '@chakra-ui/react'; +import React from 'react'; + +const DownloadSVG = ({ content, filename }) => { + const downloadFile = () => { + const url = window.URL.createObjectURL(new Blob([content])); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', filename); + document.body.appendChild(link); + link.click(); + }; + return ( + + ); +}; + +export default DownloadSVG; diff --git a/src/components/Common/Highlight.js b/src/components/Common/Highlight.js new file mode 100644 index 0000000..6e6407e --- /dev/null +++ b/src/components/Common/Highlight.js @@ -0,0 +1,34 @@ +import React from 'react'; +import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'; +import markup from 'react-syntax-highlighter/dist/esm/languages/prism/markup'; +import dart from 'react-syntax-highlighter/dist/esm/languages/prism/dart'; +import { coy, nord } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import { useColorModeValue } from '@chakra-ui/react'; + +SyntaxHighlighter.registerLanguage('markup', markup); +SyntaxHighlighter.registerLanguage('dart', dart); + +export default function Highlight({ code, lang }) { + const theme = useColorModeValue(coy, nord); + return ( + + {code} + + ); +} diff --git a/src/components/Common/Hint.js b/src/components/Common/Hint.js new file mode 100644 index 0000000..1d324c7 --- /dev/null +++ b/src/components/Common/Hint.js @@ -0,0 +1,39 @@ +import { + Button, + Popover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverTrigger, + Text, +} from '@chakra-ui/react'; +import React from 'react'; +import { QuestionIcon } from './Icons'; + +const Hint = ({ text }) => ( + + + + + + + + + + {text} + + + + +); +export default Hint; diff --git a/src/components/Common/Icons.js b/src/components/Common/Icons.js new file mode 100644 index 0000000..721fa04 --- /dev/null +++ b/src/components/Common/Icons.js @@ -0,0 +1,316 @@ +import React from 'react'; +import { Icon } from '@chakra-ui/react'; + +const FlutterIcon = (props) => ( + + + + +); + +const RightArrowIcon = (props) => ( + + + + +); + +const HtmlIcon = (props) => ( + + + + +); +const HoriIcon = (props) => ( + + + + +); +const InfoIcon = (props) => ( + + + + +); + +const QuestionIcon = (props) => ( + + + + +); +const DownloadIcon = (props) => ( + + + + +); + +const CopyIcon = (props) => ( + + + + +); + +const TrashIcon = (props) => ( + + + + +); + +const CreditsIcon = (props) => ( + + + + +); + +const UserIcon = (props) => ( + + + + +); + +const CopyrightIcon = (props) => ( + + + + +); + +const GithubIcon = (props) => ( + + + + +); + +const NpmIcon = (props) => ( + + + + +); + +const SoundIcon = (props) => ( + + + + +); + +const SoundOffIcon = (props) => ( + + + + +); + +const PaintIcon = (props) => ( + + + + +); + +const PaletteIcon = (props) => ( + + + + +); + +const PatternIcon = (props) => ( + + + + +); + +const ImageIcon = (props) => ( + + + + +); + +const LandscapeIcon = (props) => ( + + + + +); + +const BookmarkIcon = (props) => ( + + + + +); + +const SavedIcon = (props) => ( + + + + +); + +const TwitterIcon = (props) => ( + + + + +); + +const OutlineIcon = (props) => ( + + + + +); + +const LoIcon = () => ( + + + + + + + + +); + +export { + RightArrowIcon, + FlutterIcon, + HtmlIcon, + HoriIcon, + InfoIcon, + DownloadIcon, + CopyIcon, + LoIcon, + TrashIcon, + QuestionIcon, + SoundIcon, + SoundOffIcon, + GithubIcon, + CopyrightIcon, + UserIcon, + CreditsIcon, + PaintIcon, + PaletteIcon, + PatternIcon, + OutlineIcon, + ImageIcon, + LandscapeIcon, + SavedIcon, + TwitterIcon, + BookmarkIcon, + NpmIcon, +}; diff --git a/src/components/Common/Input.js b/src/components/Common/Input.js new file mode 100644 index 0000000..f384e3d --- /dev/null +++ b/src/components/Common/Input.js @@ -0,0 +1,23 @@ +import { Box, Input as ChakInput } from '@chakra-ui/react'; +import React from 'react'; +import { validateHex } from '../../utils/code.utils'; + +const Input = ({ value, onEnter }) => ( + + { + if (e.key !== 'Enter') return; + if (!validateHex(e.target.value)) return; + onEnter(e.target.value); + }} + /> + +); + +export default Input; diff --git a/src/components/Common/Logo.js b/src/components/Common/Logo.js new file mode 100644 index 0000000..acd4519 --- /dev/null +++ b/src/components/Common/Logo.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { graphql, Link, useStaticQuery } from 'gatsby'; +import Img from 'gatsby-image'; + +const Logo = ({ size = 100 }) => { + const data = useStaticQuery(graphql` + query { + file(relativePath: { eq: "blobs-logo.png" }) { + childImageSharp { + fixed(width: 100, height: 100) { + ...GatsbyImageSharpFixed + } + } + } + } + `); + + return ( + + Blobs logo + + ); +}; + +export default Logo; diff --git a/src/components/Common/Modal.js b/src/components/Common/Modal.js new file mode 100644 index 0000000..85e2eba --- /dev/null +++ b/src/components/Common/Modal.js @@ -0,0 +1,66 @@ +import { CloseIcon } from '@chakra-ui/icons'; +import { + Box, + Button, + Flex, + Modal as ChakModal, + ModalBody, + ModalContent, + ModalHeader, + ModalOverlay, + useDisclosure, + Heading, + Divider, +} from '@chakra-ui/react'; +import React from 'react'; + +const Modal = ({ title, src, children, actions, size = '2xl' }) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + return ( + <> + {src} + + + + + + + + {title} + + {actions} + + + + + + + {typeof children === 'function' ? children() : children} + + + + + ); +}; + +export default Modal; diff --git a/src/components/Common/Pallette.js b/src/components/Common/Pallette.js new file mode 100644 index 0000000..91c4d5d --- /dev/null +++ b/src/components/Common/Pallette.js @@ -0,0 +1,31 @@ +import { Box, Flex } from '@chakra-ui/react'; +import React from 'react'; + +const Pallette = ({ onClick, colors }) => { + const isGradient = colors[0].length === 2; + let colorProps = (color) => ({ bg: color }); + if (isGradient) { + colorProps = ([startCol, endCol]) => ({ + bgGradient: `linear(to-b, ${startCol}, ${endCol})`, + }); + } + + return ( + + {colors.map((color) => ( +
+ onClick(color)} + {...colorProps(color)} + /> +
+ ))} +
+ ); +}; + +export default Pallette; diff --git a/src/components/Common/Popover.js b/src/components/Common/Popover.js new file mode 100644 index 0000000..86821f9 --- /dev/null +++ b/src/components/Common/Popover.js @@ -0,0 +1,80 @@ +import { + Box, + Heading, + Popover as ChakPopover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverHeader, + PopoverTrigger, + Tooltip, +} from '@chakra-ui/react'; +import React, { useState } from 'react'; + +const Popover = ({ props, children, label, trigger }) => { + const [isOpen, setIsOpen] = useState(false); + const open = () => setIsOpen(!isOpen); + const close = () => setIsOpen(false); + + return ( + + + + {!!trigger && {trigger}} + {!trigger && ( + + + + + + )} + + + + + + + + {label} + + + + {typeof children === 'function' ? children(close) : children} + + + + ); +}; + +export default Popover; diff --git a/src/components/Common/Slider.js b/src/components/Common/Slider.js new file mode 100644 index 0000000..f181cf0 --- /dev/null +++ b/src/components/Common/Slider.js @@ -0,0 +1,49 @@ +import { + Box, + Container, + Flex, + Slider as ChakSlider, + SliderFilledTrack, + SliderThumb, + SliderTrack, + Text, + Tooltip, +} from '@chakra-ui/react'; +import React from 'react'; +import Hint from './Hint'; + +const Slider = ({ name, value, min, max, onChange, info }) => ( + + + + + {name} + + + + + + + + + + + + + + +); + +export default Slider; diff --git a/src/components/Common/UrlInput.js b/src/components/Common/UrlInput.js new file mode 100644 index 0000000..a9490bc --- /dev/null +++ b/src/components/Common/UrlInput.js @@ -0,0 +1,39 @@ +import { Box, Center, Input as ChakInput, Text } from '@chakra-ui/react'; +import React, { useState } from 'react'; + +const UrlInput = ({ value, onEnter }) => { + const [error, setError] = useState(null); + const urlRegex = /([\w+]+\:\/\/)?([\w\d-]+\.)*[\w-]+[\.\:]\w+([\/\?\=\&\#\.]?[\w-]+)*\/?/gm; + + return ( + + { + if (e.key !== 'Enter') return; + const url = e.target.value; + if (!urlRegex.test(url)) { + setError('Invalid URL'); + return; + } + setError(null); + onEnter(url); + }} + /> + {error && ( +
+ + {error} + +
+ )} +
+ ); +}; + +export default UrlInput; diff --git a/src/components/Controls/ComplexitySlider.js b/src/components/Controls/ComplexitySlider.js new file mode 100644 index 0000000..20431cf --- /dev/null +++ b/src/components/Controls/ComplexitySlider.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { dynamic } from '../../state'; +import Slider from '../Common/Slider'; + +const ComplexitySlider = ({ edges, updateEdges }) => ( + +); + +export default dynamic(ComplexitySlider, ['edges']); diff --git a/src/components/Controls/FlutterCodeModalButton.js b/src/components/Controls/FlutterCodeModalButton.js new file mode 100644 index 0000000..be4b8f0 --- /dev/null +++ b/src/components/Controls/FlutterCodeModalButton.js @@ -0,0 +1,73 @@ +/* eslint-disable react/jsx-one-expression-per-line */ +/* eslint-disable react/jsx-wrap-multilines */ +import { + Button, + IconButton, + Link, + Text, + Tooltip, + useClipboard, +} from '@chakra-ui/react'; +import React from 'react'; +import { dynamic } from '../../state'; +import Highlight from '../Common/Highlight'; +import Modal from '../Common/Modal'; +import { FlutterIcon, CopyIcon } from '../Common/Icons'; + +const FlutterCodeModalButton = ({ edges, growth, seed }) => { + const ID = `${edges}-${growth}-${seed}`; + const code = `///import blobs library +import 'package:blobs/blobs.dart'; + +///add blob widget +Container( + child: Blob.fromID( + id: ['${ID}'], + size: 400, + ), +), + `; + const { hasCopied, onCopy } = useClipboard(ID); + + const Actions = () => ( + <> + + + ); + + return ( + + + + + + } + actions={} + > + + + For more info about the package and documentation, please check the{' '} + + blobs + {' '} + repository. + + + ); +}; + +export default dynamic(FlutterCodeModalButton, ['edges', 'growth', 'seed']); diff --git a/src/components/Controls/GradientColorsPicker.js b/src/components/Controls/GradientColorsPicker.js new file mode 100644 index 0000000..d2a6db8 --- /dev/null +++ b/src/components/Controls/GradientColorsPicker.js @@ -0,0 +1,84 @@ +/* eslint-disable react/jsx-wrap-multilines */ +import React from 'react'; +import { Box, Divider, Flex } from '@chakra-ui/react'; +import { dynamic } from '../../state'; +import Pallette from '../Common/Pallette'; +import Popover from '../Common/Popover'; +import Input from '../Common/Input'; +import Ding from '../Common/Ding'; +import { PaletteIcon } from '../Common/Icons'; + +const defaultColors = [ + ['#e96443', '#904e95'], + ['#ff5f6d', '#ffc371'], + ['#eecda3', '#ef629f'], + ['#4ca1af', '#c4e0e5'], + ['#c2e59c', '#64b3f4'], + ['#3ca55c', '#b5ac49'], +]; + +const GradientColorsPicker = ({ + colors: [start, end], + switchToGradientColors, + updateGradientStartColor, + updateGradientEndColor, + type, +}) => { + const Picker = () => ( + + } + /> + ); + const Content = ({ close }) => ( + + { + switchToGradientColors(value); + close(); + }} + colors={defaultColors} + /> + + + + { + updateGradientStartColor(value); + close(); + }} + /> + + { + updateGradientEndColor(value); + close(); + }} + /> + + + + ); + return ( + } + > + {(close) => } + + ); +}; + +export default dynamic(GradientColorsPicker, ['colors', 'type']); diff --git a/src/components/Controls/HtmlCodeModalButton.js b/src/components/Controls/HtmlCodeModalButton.js new file mode 100644 index 0000000..8a9bc64 --- /dev/null +++ b/src/components/Controls/HtmlCodeModalButton.js @@ -0,0 +1,64 @@ +/* eslint-disable react/jsx-wrap-multilines */ +import { Button, IconButton, Tooltip, useClipboard } from '@chakra-ui/react'; +import React, { useState } from 'react'; +import { dynamic } from '../../state'; +import DownloadSVG from '../Common/DownloadSVG'; +import Highlight from '../Common/Highlight'; +import Modal from '../Common/Modal'; +import { HtmlIcon, CopyIcon } from '../Common/Icons'; +import { formatCode } from '../../utils/code.utils'; + +const HtmlCodeModalButton = ({ seed, edges, growth }) => { + const ID = `${edges}-${growth}-${seed}`; + const [code, setCode] = useState(null); + + const { hasCopied, onCopy } = useClipboard(code); + + const Actions = () => ( + <> + + + + ); + + const Content = () => { + const svgEl = document.getElementById('blobSvg'); + const markup = svgEl ? formatCode(svgEl.outerHTML) : ''; + + setCode(markup.replace(/^\s+|\s+$/g, '')); + return ; + }; + + return ( + + + + + + } + actions={} + > + {() => } + + ); +}; +export default dynamic(HtmlCodeModalButton, [ + 'seed', + 'edges', + 'growth', + 'svgPath', +]); diff --git a/src/components/Controls/ImageSetter.js b/src/components/Controls/ImageSetter.js new file mode 100644 index 0000000..abac11b --- /dev/null +++ b/src/components/Controls/ImageSetter.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { Box, Button } from '@chakra-ui/react'; +import { dynamic } from '../../state'; +import Popover from '../Common/Popover'; +import UrlInput from '../Common/UrlInput'; +import Ding from '../Common/Ding'; +import { ImageIcon, LandscapeIcon, RightArrowIcon } from '../Common/Icons'; + +const ImageSetter = ({ type, image, switchToImage }) => { + const Picker = () => ( + } + /> + ); + const Content = ({ close }) => ( + + { + switchToImage(value); + close(); + }} + /> + + ); + return ( + }> + {(close) => } + + ); +}; + +export default dynamic(ImageSetter, ['image', 'type']); diff --git a/src/components/Controls/OutlineToggleButton.js b/src/components/Controls/OutlineToggleButton.js new file mode 100644 index 0000000..a3f841a --- /dev/null +++ b/src/components/Controls/OutlineToggleButton.js @@ -0,0 +1,17 @@ +import { CheckCircleIcon } from '@chakra-ui/icons'; +import React from 'react'; +import { dynamic } from '../../state'; +import Ding from '../Common/Ding'; +import { OutlineIcon } from '../Common/Icons'; + +const OutlineToggleButton = ({ isOutline, toggleOutline }) => ( + } + onClick={toggleOutline} + /> +); + +export default dynamic(OutlineToggleButton, ['isOutline']); diff --git a/src/components/Controls/PatternSetter.js b/src/components/Controls/PatternSetter.js new file mode 100644 index 0000000..7c58bcc --- /dev/null +++ b/src/components/Controls/PatternSetter.js @@ -0,0 +1,89 @@ +/* eslint-disable react/jsx-wrap-multilines */ +import React from 'react'; +import { Box, useColorModeValue } from '@chakra-ui/react'; +import { dynamic } from '../../state'; +import Popover from '../Common/Popover'; +import * as Patterns from '../../patterns'; +import Ding from '../Common/Ding'; +import { PatternIcon } from '../Common/Icons'; + +const PatternBox = ({ meta, clickHandler, isSelected }) => { + const patternBg = useColorModeValue('A0AEC0', '4A5568'); + return ( + clickHandler(meta.name)} + /> + ); +}; + +const PatternSetter = ({ type, pattern, switchToPattern }) => { + const Picker = () => { + const { width = '', height = '', path = '' } = Patterns[pattern] || {}; + return ( + + } + /> + ); + }; + + const Content = ({ close }) => ( + + {Object.keys(Patterns).map((name) => ( + { + switchToPattern(value); + close(); + }} + isSelected={pattern === name} + /> + ))} + + ); + + return ( + }> + {(close) => } + + ); +}; + +export default dynamic(PatternSetter, ['pattern', 'type']); diff --git a/src/components/Controls/RandomizerButton.js b/src/components/Controls/RandomizerButton.js new file mode 100644 index 0000000..b6efd46 --- /dev/null +++ b/src/components/Controls/RandomizerButton.js @@ -0,0 +1,25 @@ +/* eslint-disable import/no-absolute-path */ +/* eslint-disable import/no-unresolved */ +import React from 'react'; +import { Button } from '@chakra-ui/react'; +import useSound from 'use-sound'; +import boopSfx from '/static/spring.mp3'; + +import { createRandomBlob } from '../../utils/blob.utils'; +import { dynamic } from '../../state'; + +const RandomizerButton = ({ playSound }) => { + const [play] = useSound(boopSfx, { volume: 0.5 }); + return ( + + ); +}; +export default dynamic(RandomizerButton, ['playSound']); diff --git a/src/components/Controls/RandomnessSlider.js b/src/components/Controls/RandomnessSlider.js new file mode 100644 index 0000000..355d488 --- /dev/null +++ b/src/components/Controls/RandomnessSlider.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { dynamic } from '../../state'; +import Slider from '../Common/Slider'; + +const RandomnessSlider = ({ growth, updateGrowth }) => ( + +); + +export default dynamic(RandomnessSlider, ['growth']); diff --git a/src/components/Controls/SolidColorPicker.js b/src/components/Controls/SolidColorPicker.js new file mode 100644 index 0000000..caad782 --- /dev/null +++ b/src/components/Controls/SolidColorPicker.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { Box, Divider } from '@chakra-ui/react'; +import { dynamic } from '../../state'; +import Pallette from '../Common/Pallette'; +import Popover from '../Common/Popover'; +import Input from '../Common/Input'; +import Ding from '../Common/Ding'; +import { PaintIcon } from '../Common/Icons'; + +const defaultColors = [ + '#00cec9', + '#fab1a0', + '#fdcb6e', + '#fd79a8', + '#a29bfe', + '#B53471', +]; + +const SolidColorPicker = ({ type, color, switchToSolidColor }) => { + const Picker = () => ( + } + /> + ); + + const Content = ({ close }) => { + const selectAndClose = (value) => { + switchToSolidColor(value); + close(); + }; + return ( + + + + + + + + ); + }; + + return ( + }> + {(close) => } + + ); +}; + +export default dynamic(SolidColorPicker, ['color', 'type']); diff --git a/src/components/Footer.js b/src/components/Footer.js new file mode 100644 index 0000000..d3a5dfc --- /dev/null +++ b/src/components/Footer.js @@ -0,0 +1,98 @@ +/* eslint-disable react/jsx-wrap-multilines */ +import { Box, Link, Text, Button, Center, HStack } from '@chakra-ui/react'; +import React from 'react'; +import { dynamic } from '../state'; +import Modal from './Common/Modal'; +import { + LoIcon, + SoundIcon, + SoundOffIcon, + GithubIcon, + UserIcon, + CopyrightIcon, + CreditsIcon, +} from './Common/Icons'; +import Credits from './Misc/Credits'; +import SourceCode from './Misc/SourceCode'; +import ThemeSwitch from './ThemeSwitch'; + +const Footer = ({ toggleSound, playSound }) => ( +
+ + + + } + aria-label="Source code" + > + Source code + + } + > + + + + + + } + aria-label="Credits" + > + Credits + + } + > + + + + + + + + + + + +
+); + +export default dynamic(Footer, ['playSound']); diff --git a/src/components/Layout.js b/src/components/Layout.js new file mode 100644 index 0000000..fb59c8b --- /dev/null +++ b/src/components/Layout.js @@ -0,0 +1,46 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { useStaticQuery, graphql } from 'gatsby'; +import { Box, Container, useColorModeValue } from '@chakra-ui/react'; +import { Provider } from 'redux-zero/react'; +import { store } from '../state'; + +import Footer from './Footer'; + +const Layout = ({ children }) => { + const theme = useColorModeValue('light', 'dark'); + const data = useStaticQuery(graphql` + query SiteTitleQuery { + site { + siteMetadata { + title + } + } + } + `); + + return ( + + + + + {children} + +