├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .gitlab-ci.yml ├── .npmignore ├── .vscode └── settings.json ├── README.md ├── package-lock.json ├── package.json ├── sandbox ├── assets │ ├── favicon.ico │ └── index.html ├── index.tsx ├── tsconfig.json ├── webpack.config.js └── webpack.production.config.js ├── src ├── Colors.ts ├── assets │ └── ezgif.com-video-to-gif.gif ├── components │ ├── atoms │ │ ├── ButtonSimple.tsx │ │ ├── Checkbox.tsx │ │ ├── IconButton.tsx │ │ └── index.tsx │ ├── editor │ │ ├── ColumnComponent.tsx │ │ ├── ColumnsComponent.tsx │ │ ├── Controls.tsx │ │ ├── DocumentComponent.tsx │ │ ├── EmptyFeatureComponent.tsx │ │ ├── FeatureComponent.tsx │ │ ├── ImageComponent.tsx │ │ ├── ListBlockComponent.tsx │ │ ├── StackComponent.tsx │ │ ├── TableBlockComponent.tsx │ │ ├── TextBlockComponent.tsx │ │ ├── TimeStampComponent.tsx │ │ ├── display │ │ │ ├── DeleteAndEdit.tsx │ │ │ ├── Rolloutable.tsx │ │ │ ├── SectionTitle.tsx │ │ │ ├── TopMenuEdit.tsx │ │ │ └── styles │ │ │ │ ├── Rolloutable.tsx │ │ │ │ └── SectionTitle.tsx │ │ ├── index.tsx │ │ └── styles │ │ │ ├── Column.tsx │ │ │ ├── Columns.tsx │ │ │ ├── Controls.tsx │ │ │ ├── Feature.tsx │ │ │ ├── Image.tsx │ │ │ ├── ListBlock.tsx │ │ │ ├── Stack.tsx │ │ │ ├── TableBlock.tsx │ │ │ └── TextBlock.tsx │ ├── icons │ │ └── index.tsx │ ├── index.tsx │ ├── molecules │ │ └── Confirm.tsx │ └── styles │ │ ├── ButtonSimple.tsx │ │ ├── CheckboxStyles.tsx │ │ ├── IconButton.tsx │ │ └── TopMenu.tsx ├── constants.ts ├── frontend-types.ts ├── graphql-zeus.ts ├── index.tsx ├── livepdf │ ├── PDFDocument.tsx │ ├── fonts │ │ ├── FiraSans-Bold.ttf │ │ ├── FiraSans-Light.ttf │ │ ├── FiraSans-Medium.ttf │ │ └── FiraSans-Regular.ttf │ ├── index.tsx │ ├── livecomponents │ │ └── editor │ │ │ ├── ColumnComponent.tsx │ │ │ ├── ColumnsComponent.tsx │ │ │ ├── FeatureComponent.tsx │ │ │ ├── ImageComponent.tsx │ │ │ ├── ListBlockComponent.tsx │ │ │ ├── StackComponent.tsx │ │ │ ├── TableBlockComponent.tsx │ │ │ ├── TextBlockComponent.tsx │ │ │ ├── TimeStampComponent.tsx │ │ │ └── index.tsx │ └── styles │ │ └── index.tsx ├── models │ ├── getString.ts │ ├── index.ts │ └── languages │ │ ├── en.ts │ │ └── pl.ts ├── screens │ ├── InitialPDFModels.tsx │ ├── ReactPDFEditor.tsx │ ├── index.tsx │ └── styles │ │ └── ReactPDFEditor.tsx ├── topmenu │ ├── components │ │ ├── ExpandableInput.tsx │ │ ├── SelectInput.tsx │ │ ├── SmallInput.tsx │ │ ├── Tooltip.tsx │ │ ├── TopIcon.tsx │ │ ├── index.tsx │ │ └── styles │ │ │ ├── SelectInput.tsx │ │ │ ├── Shared.tsx │ │ │ ├── SmallInput.tsx │ │ │ └── Topicon.tsx │ ├── index.tsx │ ├── items │ │ ├── ExpandableInputMaximize2.tsx │ │ ├── ExpandableInputMinimize2.tsx │ │ ├── ExpandableInputSquare.tsx │ │ ├── InputFontSize.tsx │ │ ├── InputLineHeight.tsx │ │ ├── SelectFontType.tsx │ │ ├── TopIconAlignCenter.tsx │ │ ├── TopIconAlignJustify.tsx │ │ ├── TopIconAlignLeft.tsx │ │ ├── TopIconAlignRight.tsx │ │ ├── TopIconBold.tsx │ │ ├── TopIconFontSize.tsx │ │ ├── TopIconItalic.tsx │ │ ├── TopIconRedo.tsx │ │ ├── TopIconTextDecoration.tsx │ │ ├── TopIconUndo.tsx │ │ ├── alignItemsToBaselineIcon.tsx │ │ ├── alignItemsToCenterIcon.tsx │ │ ├── alignItemsToEndIcon.tsx │ │ ├── alignItemsToStartIcon.tsx │ │ ├── alignItemsToStretchIcon.tsx │ │ ├── alignSelfToFlexBaselineIcon.tsx │ │ ├── alignSelfToFlexCenterIcon.tsx │ │ ├── alignSelfToFlexEndIcon.tsx │ │ ├── alignSelfToFlexStartIcon.tsx │ │ ├── alignSelfToFlexStretchIcon.tsx │ │ ├── fitToParentIcon.tsx │ │ ├── flexFlowColumnIcon.tsx │ │ ├── flexFlowRowIcon.tsx │ │ ├── index.tsx │ │ ├── justifyContentCenterIcon.tsx │ │ ├── justifyContentFlexEndIcon.tsx │ │ ├── justifyContentFlexStartIcon.tsx │ │ ├── justifyContentSpaceAround.tsx │ │ └── justifyContentSpaceBetween.tsx │ └── models │ │ └── index.ts ├── utils.ts └── zeusSelectionSets.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-react"], 3 | "rules": { 4 | "arrow-parens": 0, 5 | "arrow-return-shorthand": [false], 6 | "comment-format": [true, "check-space"], 7 | "import-blacklist": [true, "rxjs"], 8 | "interface-over-type-literal": false, 9 | "interface-name": false, 10 | "max-line-length": [false], 11 | // "max-line-length": [true, 120], 12 | "member-access": false, 13 | "member-ordering": [true, { "order": "fields-first" }], 14 | "newline-before-return": 1, 15 | "no-any": false, 16 | "no-empty-interface": false, 17 | "no-import-side-effect": [false], 18 | "no-inferrable-types": [true, "ignore-params", "ignore-properties"], 19 | "no-invalid-this": [true, "check-function-in-method"], 20 | "no-null-keyword": false, 21 | "no-require-imports": false, 22 | "no-submodule-imports": [true, "@src", "rxjs"], 23 | "no-this-assignment": [true, { "allow-destructuring": true }], 24 | "no-trailing-whitespace": true, 25 | "no-unused-variable": [true, "react"], 26 | "no-shadowed-variable": false, 27 | // "no-magic-numbers": true, 28 | "no-console": 0, 29 | "object-literal-sort-keys": false, 30 | "object-literal-shorthand": false, 31 | "object-property-newline": 0, 32 | "one-variable-per-declaration": [false], 33 | "only-arrow-functions": [true, "allow-declarations"], 34 | "ordered-imports": [false], 35 | "prefer-method-signature": false, 36 | "prefer-template": [true, "allow-single-concat"], 37 | "quotemark": [true, "single", "jsx-double"], 38 | "semicolon": [false, "never", "ignore-interfaces"], 39 | "trailing-comma": [ 40 | true, 41 | { 42 | "singleline": "never", 43 | "multiline": { 44 | "objects": "never", 45 | "arrays": "always", 46 | "functions": "never", 47 | "typeLiterals": "ignore" 48 | }, 49 | "esSpecCompliant": true 50 | } 51 | ], 52 | "triple-equals": [true, "allow-null-check"], 53 | "type-literal-delimiter": false, 54 | "typedef": [true, "parameter", "property-declaration"], 55 | "variable-name": [ 56 | true, 57 | "ban-keywords", 58 | "check-format", 59 | "allow-pascal-case", 60 | "allow-leading-underscore" 61 | ], 62 | "jsx-no-lambda": false, 63 | "jsx-no-multiline-js": false, 64 | "jsx-a11y/anchor-is-valid": 0, 65 | "no-useless-escape": 0, 66 | "prefer-const": 0, 67 | "prefer-object-spread": true, 68 | "class-name": true, 69 | "eofline": true 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.gif filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /public 14 | /lib 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | tsconfig.tsbuildinfo -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: node:10 2 | build: 3 | # only: 4 | # - develop 5 | artifacts: 6 | paths: 7 | - public 8 | stage: build 9 | script: 10 | - npm i 11 | - npm run build 12 | pages: 13 | artifacts: 14 | paths: 15 | - public 16 | script: 17 | - echo "Hello" -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_STORE 3 | .docz 4 | .tscache 5 | tsconfig.tsbuildinfo 6 | node_modules 7 | .module-cache 8 | *.log* 9 | build 10 | dist 11 | src 12 | .idea 13 | __test__ 14 | __mock__ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React PDF-editor 2 | 3 | 4 | [![N|Solid](https://cldup.com/dTxpPi9lDf.thumb.png)](https://nodesource.com/products/nsolid) 5 | 6 | [![Build Status](https://travis-ci.org/joemccann/dillinger.svg)](https://react-pdf-editor.brh.cloud/) 7 | 8 | --- 9 | 10 | The Portable Document Format, or PDF, is everywhere. 11 | 12 | PDF is a simple file format that can be used to read documents on regular people’s computers. 13 | 14 | PDF has become a global standard for more secure and dependable information exchange since 1993. 15 | 16 | Government and private industry rely on secure PDF-based storage of electronic records that can be reliably shared. 17 | 18 | If you want to cut down on the mountains of paper being produced by offices, React PDF Editor is a solution for you. 19 | 20 | Creating PDF in React PDF Editor takes a short amount of time and gives you a lot of advantages. 21 | 22 | That is why React PDF Editor will help you create PDF files - one of the most important way business users shared documents. 23 | 24 | 25 | React PDF Editor can be used for: 26 | - creating CVs, 27 | - electronic publishing, 28 | - user manuals, 29 | - invoices, 30 | - books, 31 | - non editable advertising materials, 32 | - archiving 33 | 34 | ![](/src/assets/ezgif.com-video-to-gif.gif) 35 | 36 | ### Real-time online PDF editor, based on ReactJs and TypeScript. 37 | 38 | ### Easy to use. 39 | 40 | ### Fast and light. 41 | 42 | --- 43 | 44 | # Installation 45 | 46 | React PDF-ediotr requires: 47 | - [Node.js](https://nodejs.org/) v11.6.0+ 48 | - [npm](https://www.npmjs.com/) 49 | 50 | --- 51 | 52 | ### React PDF-ediotr is very easy to install. 53 | 54 | # Install 55 | 56 | ```sh 57 | $ npm i react-pdf-editor 58 | $ npm start 59 | ``` 60 | Install the dependencies and devDependencies and start the server. 61 | 62 | # Develop Instalation 63 | 64 | ```sh 65 | $ git clone https://github.com/aexol-studio/react-pdf-editor.git 66 | $ cd react-pdf-editor 67 | $ npm i 68 | $ npm start 69 | ``` 70 | --- 71 | ### Not Familiar with Git? 72 | 73 | Click [here](https://github.com/aexol-studio/react-pdf-editor.git/) then download the .zip file. Extract the contents of the zip file, then open your terminal, change to the project directory, and: 74 | 75 | > npm install 76 | > npm start 77 | 78 | 79 | Verify the deployment by navigating to your server address in your preferred browser. 80 | 81 | ```sh 82 | http://localhost:1568/ 83 | ``` 84 | --- 85 | 86 | ### Demo 87 | 88 | 89 | Here is working [live demo](https://react-pdf-editor.brh.cloud/). 90 | 91 | --- 92 | 93 | ### Built With 94 | 95 | ReactJs - The JavaScript library for building user interfaces 96 | NodeJs - The JavaScript runtime built 97 | TypeScript - Is a language for application-scale JavaScript. 98 | TypeStyle - Writing CSS with TypeStyle will be just as fluent as writing JavaScript with TypeScript. 99 | Webpack - The module bundler for modern JavaScript applications 100 | 101 | 102 | License 103 | ---- 104 | 105 | MIT 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-pdf-editor", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@react-pdf/renderer": "^1.6.17", 6 | "classnames": "^2.3.1", 7 | "rc-color-picker": "^1.2.6", 8 | "rc-tooltip": "^4.2.3", 9 | "re-resizable": "^6.9.9", 10 | "react-feather": "^2.0.9", 11 | "typestyle": "^2.3.0" 12 | }, 13 | "peerDependencies": { 14 | "react": ">=16.8.6", 15 | "react-dom": ">=16.8.6" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "7.4.3", 19 | "@babel/preset-env": "^7.17.12", 20 | "@svgr/webpack": "4.1.0", 21 | "@types/classnames": "^2.3.1", 22 | "@types/react": "^16.14.26", 23 | "@types/react-dom": "^16.9.16", 24 | "@typescript-eslint/eslint-plugin": "1.6.0", 25 | "@typescript-eslint/parser": "1.6.0", 26 | "case-sensitive-paths-webpack-plugin": "2.2.0", 27 | "css-loader": "2.1.1", 28 | "eslint": "^5.16.0", 29 | "eslint-config-react-app": "^4.0.0", 30 | "eslint-loader": "2.1.2", 31 | "eslint-plugin-import": "2.16.0", 32 | "eslint-plugin-jsx-a11y": "6.2.1", 33 | "eslint-plugin-react": "7.12.4", 34 | "eslint-plugin-react-hooks": "^1.5.0", 35 | "file-loader": "3.0.1", 36 | "graphql-zeus": "^2.8.6", 37 | "html-loader": "^0.5.5", 38 | "html-webpack-plugin": "^3.2.0", 39 | "mini-css-extract-plugin": "0.5.0", 40 | "optimize-css-assets-webpack-plugin": "5.0.1", 41 | "react": "^16.14.0", 42 | "react-dom": "^16.14.0", 43 | "style-loader": "0.23.1", 44 | "ts-loader": "^6.2.2", 45 | "tsconfig-paths-webpack-plugin": "^3.5.2", 46 | "typescript": "^3.9.10", 47 | "url-loader": "^2.3.0", 48 | "webpack": "^4.46.0", 49 | "webpack-cleanup-plugin": "^0.5.1", 50 | "webpack-cli": "^3.3.12", 51 | "webpack-dev-server": "^3.11.3", 52 | "webpack-manifest-plugin": "2.0.4" 53 | }, 54 | "scripts": { 55 | "start": "webpack-dev-server --config ./sandbox/webpack.config.js --content-base ./sandbox/ --port 1568 --hot --inline --open", 56 | "build": "webpack -p --config ./sandbox/webpack.production.config.js --content-base ./sandbox/", 57 | "watch": "tsc --watch" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sandbox/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/33dc039c17d9fd5f911fa2f874f4afa5825809b1/sandbox/assets/favicon.ico -------------------------------------------------------------------------------- /sandbox/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | PDF Editor 8 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /sandbox/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { style } from "typestyle"; 4 | import { ReactPDFEditor } from "../src"; 5 | 6 | 7 | export const AppContainer = style({ 8 | $debugName: "AppContainer", 9 | fontFamily: "Fira Sans", 10 | height: "100vh", 11 | width: "100%", 12 | margin: 0 13 | }); 14 | 15 | const App: React.FC = () => { 16 | return ( 17 |
18 | 19 |
20 | ); 21 | }; 22 | 23 | ReactDOM.render(, document.getElementById("root")); 24 | -------------------------------------------------------------------------------- /sandbox/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "target": "es6", 5 | "jsx": "react", 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "esModuleInterop": true, 10 | "removeComments": true, 11 | "noUnusedLocals": true, 12 | "skipLibCheck": true, 13 | "incremental": true, 14 | "strict": true, 15 | "outDir": "./lib", 16 | "lib": ["es6", "es7", "esnext", "dom"], 17 | "baseUrl": "./", 18 | "rootDir": "../" 19 | }, 20 | "exclude": ["../lib", "../build", "../node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /sandbox/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var path = require("path"); 3 | var sourcePath = path.join(__dirname, "./"); 4 | var outPath = path.join(__dirname, "../public"); 5 | var HtmlWebpackPlugin = require("html-webpack-plugin"); 6 | var WebpackCleanupPlugin = require("webpack-cleanup-plugin"); 7 | const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); 8 | // const config = require('./config.js') 9 | 10 | module.exports = { 11 | context: sourcePath, 12 | entry: { 13 | app: "./index.tsx" 14 | }, 15 | output: { 16 | path: outPath, 17 | filename: "bundle[hash].js", 18 | chunkFilename: "[chunkhash].js", 19 | publicPath: "/" 20 | }, 21 | target: "web", 22 | mode: "development", 23 | resolve: { 24 | plugins: [new TsconfigPathsPlugin({/* options: see below */})], 25 | extensions: [".js", ".jsx", ".ts", ".tsx"], 26 | mainFields: ["module", "browser", "main"], 27 | alias: { 28 | app: path.resolve(__dirname, "../src") 29 | } 30 | }, 31 | module: { 32 | rules: [ 33 | { 34 | test: /\.css$/i, 35 | use: ["style-loader", "css-loader"] 36 | }, 37 | { 38 | test: /\.html$/, 39 | use: "html-loader" 40 | }, 41 | { 42 | test: /\.tsx?$/, 43 | loader: "ts-loader" 44 | }, 45 | { test: /\.(png|svg)$/, use: "url-loader?limit=10000" }, 46 | { test: /\.(jpg|gif)$/, use: "file-loader" }, 47 | { 48 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, 49 | loader: "url-loader?limit=10000&mimetype=application/font-woff" 50 | }, 51 | { 52 | test: /\.(ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/, 53 | loader: "file-loader?name=[name].[ext]" 54 | } 55 | ] 56 | }, 57 | plugins: [ 58 | new webpack.EnvironmentPlugin({ 59 | NODE_ENV: "development" 60 | }), 61 | new WebpackCleanupPlugin(), 62 | new HtmlWebpackPlugin({ 63 | template: "./assets/index.html" 64 | }) 65 | ], 66 | devServer: { 67 | contentBase: sourcePath, 68 | hot: true, 69 | inline: true, 70 | historyApiFallback: { 71 | disableDotRule: true 72 | }, 73 | stats: "minimal" 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /sandbox/webpack.production.config.js: -------------------------------------------------------------------------------- 1 | const { devServer, ...webpackParams } = require("./webpack.config"); 2 | 3 | module.exports = { 4 | ...webpackParams, 5 | mode: "production" 6 | }; 7 | -------------------------------------------------------------------------------- /src/Colors.ts: -------------------------------------------------------------------------------- 1 | export const Colors = { 2 | /* Black*/ 3 | 4 | Black: "#000000", 5 | 6 | /* White */ 7 | 8 | White: "#FFFFFF", 9 | /* Damsel */ 10 | 11 | "Damsel in distress": "#E1E0FF", 12 | 13 | /* Mora */ 14 | 15 | Mora: "#A9A5FF", 16 | 17 | /* Super Nova */ 18 | 19 | "Super Nova": "#645EE1", 20 | 21 | /* Ultrasonic */ 22 | 23 | Ultrasonic: "#4A45B9", 24 | 25 | /* Pinoneer */ 26 | 27 | Pioneer: "#343096", 28 | 29 | /* Unknown */ 30 | 31 | Unknown: "#050254", 32 | 33 | /* Foggy */ 34 | 35 | Foggy: "#DFE6F7", 36 | 37 | /* Lunatic */ 38 | 39 | Lunatic: "#4168FF", 40 | 41 | /* Infinito */ 42 | 43 | Infinito: "#1844ED", 44 | 45 | /* Gravity */ 46 | 47 | Gravity: "#1539C6", 48 | 49 | /* Outer Space */ 50 | 51 | "Outer Space": "#061241", 52 | 53 | /* Ozone Layer */ 54 | 55 | "Ozone Layer": "#E1F3FF", 56 | 57 | /* Pacific */ 58 | 59 | Pacific: "#359FFB", 60 | 61 | /* Space Pirate */ 62 | 63 | "Space Pirate": "#008BF5", 64 | 65 | /* Bored Martian */ 66 | 67 | "Bored Martian": "#0D74C2", 68 | 69 | /* The Nothing */ 70 | 71 | "The Nothing": "#012744", 72 | 73 | /* Cloud 13 */ 74 | 75 | "Cloud 13": "#CCFFFC", 76 | 77 | /* Triton */ 78 | 79 | Triton: "#51E1D8", 80 | 81 | /* Summer Triangle */ 82 | 83 | "Summer Triangle": "#38C1B8", 84 | 85 | /* Inspector */ 86 | 87 | Inspector: "#04A197", 88 | 89 | /* Deep Green */ 90 | 91 | "Deep Green": "#01312E", 92 | 93 | /* Plutonic Briza */ 94 | 95 | "Plutonic Briza": "#E3FFEB", 96 | 97 | /* Cooler */ 98 | 99 | Cooler: "#56DA67", 100 | 101 | /* Serpentine */ 102 | 103 | Serpentine: "#10C642", 104 | 105 | /* Kriptonita */ 106 | 107 | Kriptonita: "#0BA134", 108 | 109 | /* Zubr */ 110 | 111 | Zubr: "#01270B", 112 | 113 | /* Captain Parrot */ 114 | 115 | "Captain Parrot": "#FFF6D5", 116 | 117 | /* Venus */ 118 | 119 | Venus: "#EAC819", 120 | 121 | /* Fireball */ 122 | 123 | Fireball: "#D4AD25", 124 | 125 | /* Collysion */ 126 | 127 | Collysion: "#A38311", 128 | 129 | /* Syndrom */ 130 | 131 | Syndrom: "#473803", 132 | 133 | /* Cotton Candy */ 134 | 135 | "Cotton Candy": "#FFE7EB", 136 | 137 | /* Muse */ 138 | 139 | Muse: "#FF5E6C", 140 | 141 | /* Cherry Bomb */ 142 | 143 | "Cherry Bomb": "#F9193F", 144 | 145 | /* ACME */ 146 | 147 | ACME: "#D11031", 148 | 149 | /* Dry Blood */ 150 | 151 | "Dry Blood": "#43030E", 152 | 153 | /* Mutant Peach */ 154 | 155 | "Mutant Peach": "#FFD9CD", 156 | 157 | /* Floating Mandarines */ 158 | 159 | "Floating Mandarines": "#FB6B3D", 160 | 161 | /* No Name */ 162 | 163 | "No Name": "#E45021", 164 | 165 | /* Flying Tomatoes */ 166 | 167 | "Flying Tomatoes": "#A52E08", 168 | 169 | /* I Need Coffee */ 170 | 171 | "I Need Coffee": "#461303", 172 | 173 | /* Meteor */ 174 | 175 | Meteor: "#FFE3C8", 176 | 177 | /* Mars */ 178 | 179 | Mars: "#F19037", 180 | 181 | /* Rising Embers */ 182 | 183 | "Rising Embers": "#E57C1A", 184 | 185 | /* Helix Nebula */ 186 | 187 | "Helix Nebula": "#C36106", 188 | 189 | /* Laika */ 190 | 191 | Laika: "#3F1F01", 192 | 193 | /* Black Hole */ 194 | 195 | "Black Hole": "#25222E", 196 | 197 | /* Dark Side */ 198 | 199 | "Dark Side": "#313132", 200 | 201 | /* Androgyn */ 202 | 203 | Androgyn: "#4B4B4C", 204 | 205 | /* Acient Stone */ 206 | 207 | "Ancient Stone": "#757575", 208 | 209 | /* Ashes */ 210 | 211 | Ashes: "#999DA0", 212 | 213 | /* Sopel */ 214 | 215 | Sopel: "#D9E0E1", 216 | 217 | /* Lead */ 218 | 219 | Lead: "#514E5A", 220 | 221 | /* Alien Blood */ 222 | 223 | "Alien Blood": "linear-gradient(197.27deg, #0091FF 0%, #5D2EEB 98.38%);", 224 | 225 | /* Space Monkeys */ 226 | 227 | "Space Monkeys": "linear-gradient(197.67deg, #0091FF -21.75%, #3E1E9F 100%);" 228 | } as const; 229 | -------------------------------------------------------------------------------- /src/assets/ezgif.com-video-to-gif.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:263c90a87843ad53d741a4f3f7b6afca4b97c6e7e1e1228ef95688fe8811e809 3 | size 1359917 4 | -------------------------------------------------------------------------------- /src/components/atoms/ButtonSimple.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as styles from "@styles/ButtonSimple"; 3 | export interface ButtonSimpleProps { 4 | onClick: () => void; 5 | children: React.ReactNode; 6 | } 7 | 8 | export const ButtonSimple = ({ children, onClick }: ButtonSimpleProps) => ( 9 |
10 | {children} 11 |
12 | ); 13 | -------------------------------------------------------------------------------- /src/components/atoms/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import classnames from "classnames"; 3 | import { CheckBox } from "@components/icons" 4 | import * as styles from "@styles/CheckboxStyles"; 5 | 6 | export interface ICheckBoxProps { 7 | value?: boolean; 8 | checkboxParagraph?: string | JSX.Element; 9 | onChange?: (value: boolean) => void; 10 | style?: React.CSSProperties; 11 | } 12 | 13 | interface ICheckboxState { 14 | value: boolean; 15 | } 16 | 17 | export class Checkbox extends React.PureComponent< 18 | ICheckBoxProps, 19 | ICheckboxState 20 | > { 21 | constructor(props: ICheckBoxProps) { 22 | super(props); 23 | this.state = { 24 | value: props.value || false 25 | }; 26 | } 27 | handleOnClick = (value: boolean) => { 28 | if (this.state.value !== value) { 29 | this.setState({ 30 | value: value 31 | }); 32 | if (this.props.onChange) { 33 | this.props.onChange(value); 34 | } 35 | } 36 | }; 37 | renderElements = () => { 38 | const { style } = this.props; 39 | return ( 40 | 41 |
this.handleOnClick(!this.state.value)} 51 | > 52 | 53 |
54 |

55 | {this.props.checkboxParagraph} 56 |

57 |
58 | ); 59 | }; 60 | render() { 61 | return
{this.renderElements()}
; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/components/atoms/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as styles from "@styles/IconButton"; 3 | export interface IconButtonProps { 4 | onClick: () => void; 5 | children: React.ReactNode; 6 | } 7 | 8 | export const IconButton = ({ children, onClick }: IconButtonProps) => ( 9 |
10 | {children} 11 |
12 | ); 13 | -------------------------------------------------------------------------------- /src/components/atoms/index.tsx: -------------------------------------------------------------------------------- 1 | export { ButtonSimple } from "./ButtonSimple"; 2 | export { Checkbox } from "./Checkbox"; 3 | export { IconButton } from "./IconButton"; -------------------------------------------------------------------------------- /src/components/editor/ColumnComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import { FeatureComponent } from "./FeatureComponent"; 4 | import * as styles from "./styles/Column"; 5 | import { Controls } from "./Controls"; 6 | export interface ColumnComponentProps { 7 | column: PartialObjects["Column"]; 8 | onChange: () => void; 9 | onDelete: () => void; 10 | onEdit: (feature: PartialObjects["Feature"]) => void; 11 | } 12 | export const ColumnComponent = ({ 13 | onChange, 14 | onEdit, 15 | column, 16 | onDelete 17 | }: ColumnComponentProps) => { 18 | return ( 19 |
20 | {column.content && Object.keys(column.content).length > 0 ? ( 21 | 27 | ) : ( 28 | { 32 | column.content = a; 33 | } 34 | } as any 35 | } 36 | mutateWholeObject={onChange} 37 | clean={() => { 38 | column.content = {}; 39 | onChange(); 40 | }} 41 | /> 42 | )} 43 |
44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /src/components/editor/ColumnsComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import * as styles from "./styles/Columns"; 4 | import { ColumnComponent } from "./ColumnComponent"; 5 | import { Colors } from "@/Colors"; 6 | import { Resizable } from "re-resizable"; 7 | import { translated } from "@/models"; 8 | import * as Icons from "react-feather"; 9 | import * as styles1 from "./styles/Feature"; 10 | import cx from "classnames"; 11 | export interface ColumnsComponentProps { 12 | columns: PartialObjects["Columns"]; 13 | onChange: () => void; 14 | onDelete: () => void; 15 | onEdit: (feature: PartialObjects["Feature"]) => void; 16 | onResize: (index: number, newWidth: number) => void; 17 | widths: string[]; 18 | } 19 | 20 | const t = translated("ColumnsCoponentTxt"); 21 | 22 | export const ColumnsComponent = ({ 23 | onChange, 24 | onEdit, 25 | onDelete, 26 | onResize, 27 | columns, 28 | widths 29 | }: ColumnsComponentProps) => { 30 | return ( 31 | <> 32 | {/* Delete row */} 33 |
34 | 39 | 40 |

{t("IconTrashDelete")}

41 |
42 |
43 | {columns.columns!.map((c, i) => { 44 | const flexBasis = widths[i]!; 45 | return ( 46 | <> 47 | { 64 | const parentRect = ref.parentElement!.parentElement!.getBoundingClientRect(); 65 | const parentWidth = Math.abs( 66 | parentRect.left - parentRect.right 67 | ); 68 | const rect = ref!.getBoundingClientRect(); 69 | const rectWidth = Math.abs(rect.left - rect.right); 70 | onResize(i, (100.0 * rectWidth) / parentWidth); 71 | }} 72 | > 73 |
79 | { 85 | columns.columns!.splice(i, 1); 86 | onChange(); 87 | }} 88 | /> 89 |
90 |
91 | 92 | ); 93 | })} 94 |
95 | 96 | ); 97 | }; 98 | -------------------------------------------------------------------------------- /src/components/editor/Controls.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import * as styles from "./styles/Controls"; 3 | import { BuiltInStyles, PartialObjects } from "@/graphql-zeus"; 4 | import { PlusCircle, MinusCircle } from "react-feather"; 5 | import { translated } from "@/models"; 6 | export interface ControlsProps { 7 | features: PartialObjects["Feature"][]; 8 | mutateWholeObject: () => void; 9 | clean: () => void; 10 | document?: boolean; 11 | parent?: PartialObjects["Feature"]; 12 | show?: boolean; 13 | } 14 | 15 | const t = translated("ControlsComponentTxt"); 16 | 17 | export const ControlsButton = ({ 18 | onClick, 19 | children 20 | }: { 21 | onClick: () => void; 22 | children: React.ReactNode; 23 | }) => { 24 | return ( 25 |
26 | {children} 27 |
28 | ); 29 | }; 30 | export const Controls = ({ 31 | features, 32 | mutateWholeObject, 33 | document, 34 | parent, 35 | show 36 | }: ControlsProps) => { 37 | // const [open, setOpen] = useState(false); 38 | 39 | const [open, setOpen] = useState(!!show); 40 | 41 | //pierwotny stan jest na false 42 | 43 | return ( 44 |
45 |
{ 48 | if (parent && parent.__typename === "ListBlock") { 49 | features.push({ 50 | text: "", 51 | __typename: "TextBlock" 52 | } as PartialObjects["TextBlock"]); 53 | mutateWholeObject(); 54 | return; 55 | } 56 | setOpen(!open); 57 | 58 | //zmiana stanu na true i pokazują się kontrolki 59 | }} 60 | > 61 | {open ? : } 62 | 63 | {/* jeśli open jest true to minus a jesli nie to plus */} 64 |
65 | {open && ( 66 | <> 67 |
setOpen(false)}> 68 |
69 |
70 | {document && ( 71 | { 73 | features.push({ 74 | __typename: "Stack", 75 | items: [] 76 | } as PartialObjects["Stack"]); 77 | setOpen(false); 78 | mutateWholeObject(); 79 | }} 80 | > 81 |

87 | {t("ButtonStack")} 88 |

89 |
90 | )} 91 | {!document && ( 92 | <> 93 | { 95 | features.push({ 96 | text: "", 97 | __typename: "TextBlock" 98 | } as PartialObjects["TextBlock"]); 99 | setOpen(false); 100 | mutateWholeObject(); 101 | }} 102 | > 103 | {/* {ControlsComponentTxt.ButtonText} */} 104 | {t("ButtonText")} 105 | 106 | { 108 | features.push({ 109 | __typename: "Image" 110 | } as PartialObjects["Image"]); 111 | setOpen(false); 112 | mutateWholeObject(); 113 | }} 114 | > 115 | {t("ButtonImage")} 116 | 117 | { 119 | features.push({ 120 | __typename: "Stack", 121 | items: [] 122 | } as PartialObjects["Stack"]); 123 | mutateWholeObject(); 124 | setOpen(false); 125 | }} 126 | > 127 | {t("ButtonStack")} 128 | 129 | { 131 | features.push({ 132 | __typename: "ListBlock", 133 | items: [] 134 | } as PartialObjects["ListBlock"]); 135 | setOpen(false); 136 | mutateWholeObject(); 137 | }} 138 | > 139 | {t("ButtonList")} 140 | 141 | 142 | { 144 | features.push({ 145 | __typename: "TableBlock", 146 | style: BuiltInStyles.NO_BORDERS_TABLE, 147 | widths: [], 148 | rows: [] 149 | } as PartialObjects["TableBlock"]); 150 | setOpen(false); 151 | mutateWholeObject(); 152 | }} 153 | > 154 | {/* //trzeba stworzyć nowy komponent VerticalTable 155 | poniewaz teraz na podstawie jednego komponentu budowane są dwa komponenty? */} 156 | 157 | {/* {ControlsComponentTxt.ButtonVerticalSplit} */} 158 | 159 | {t("ButtonVerticalSplit")} 160 | 161 | 162 | { 164 | features.push({ 165 | __typename: "TableBlock", 166 | style: BuiltInStyles.LIGHT_BORDER_TABLE, 167 | widths: [], 168 | rows: [] 169 | } as PartialObjects["TableBlock"]); 170 | setOpen(false); 171 | mutateWholeObject(); 172 | }} 173 | > 174 | {t("ButtonTable")} 175 | 176 | 177 | { 179 | features.push({ 180 | __typename: "TimeStamp" 181 | } as PartialObjects["TimeStamp"]); 182 | setOpen(false); 183 | mutateWholeObject(); 184 | }} 185 | > 186 | {t("ButtonTimeStamp")} 187 | 188 | 189 | )} 190 |
191 |
192 |
193 | 194 | )} 195 |
196 | ); 197 | }; 198 | -------------------------------------------------------------------------------- /src/components/editor/DocumentComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import { Editor } from ".."; 4 | import { Rolloutable } from "./display/Rolloutable"; 5 | import { Confirm } from "@components/molecules/Confirm"; 6 | import * as Icons from "react-feather"; 7 | import * as styles from "./styles/Feature"; 8 | import cx from "classnames"; 9 | 10 | export const DocumentComponent = ({ 11 | i, 12 | doc, 13 | onEdit, 14 | onChange, 15 | onDelete 16 | }: { 17 | i: number; 18 | doc: PartialObjects["Document"]; 19 | onChange: () => void; 20 | onDelete: () => void; 21 | onEdit: (feature: PartialObjects["Feature"]) => void; 22 | }) => { 23 | return ( 24 | 29 | {/*
*/} 30 |
31 | 32 | 36 | 37 |
38 | {doc.features!.items!.map((item, index) => ( 39 | { 45 | doc.features!.items!.splice(index, 1); 46 | onChange(); 47 | }} 48 | /> 49 | ))} 50 | { 55 | doc.features!.items = []; 56 | onChange(); 57 | }} 58 | /> 59 | 60 | ); 61 | }; 62 | -------------------------------------------------------------------------------- /src/components/editor/EmptyFeatureComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import { Controls } from "./Controls"; 4 | export interface EmptyFeatureComponentProps { 5 | feature: PartialObjects["Feature"]; 6 | onChange: () => void; 7 | onEdit: (feature: PartialObjects["Feature"]) => void; 8 | } 9 | 10 | export const EmptyFeatureComponent = ({ 11 | onChange, 12 | onEdit, 13 | feature 14 | }: EmptyFeatureComponentProps) => ( 15 | { 19 | feature = o; 20 | } 21 | } as any 22 | } 23 | show={true} 24 | //showTopMenu={true} 25 | mutateWholeObject={onChange} 26 | clean={() => { 27 | feature = {}; 28 | onChange(); 29 | }} 30 | /> 31 | ); 32 | -------------------------------------------------------------------------------- /src/components/editor/FeatureComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import { 4 | isTextBlock, 5 | isImage, 6 | isStack, 7 | isListBlock, 8 | isTableBlock, 9 | isTimeStamp 10 | } from "@/utils"; 11 | import { TextBlockComponent } from "./TextBlockComponent"; 12 | import { ImageComponent } from "./ImageComponent"; 13 | import { StackComponent } from "./StackComponent"; 14 | import { TableBlockComponent } from "./TableBlockComponent"; 15 | import { ListBlockComponent } from "./ListBlockComponent"; 16 | import * as styles from "./styles/Feature"; 17 | import { EmptyFeatureComponent } from "./EmptyFeatureComponent"; 18 | import { TimeStampComponent } from "./TimeStampComponent"; 19 | import { DeleteAndEditIconsComponent } from "./display/DeleteAndEdit"; 20 | export interface BaseFeatureComponentProps { 21 | onChange: () => void; 22 | } 23 | export interface FeatureComponentProps extends BaseFeatureComponentProps { 24 | feature: PartialObjects["Feature"]; 25 | onDelete: () => void; 26 | onEdit: (feature: PartialObjects["Feature"]) => void; 27 | onMoveDown?: () => void; 28 | onMoveUp?: () => void; 29 | [k: string]: unknown; 30 | } 31 | 32 | { 33 | } 34 | 35 | /// tutaj jest komponen wyświetlany itp itp itp 36 | 37 | const FeatureComp = (props: FeatureComponentProps) => { 38 | const { feature, onChange, onEdit, onDelete, onMoveDown, onMoveUp } = props; 39 | if (isTimeStamp(feature)) { 40 | return ( 41 |
42 | 43 | 44 |
45 | ); 46 | } 47 | if (isTextBlock(feature)) { 48 | return ( 49 | 55 | ); 56 | } 57 | if (isImage(feature)) { 58 | return ( 59 | 65 | ); 66 | } 67 | if (isStack(feature)) { 68 | return ( 69 | 78 | ); 79 | } 80 | if (isTableBlock(feature)) { 81 | return ( 82 | 89 | ); 90 | } 91 | if (isListBlock(feature)) { 92 | return ( 93 | 100 | ); 101 | } 102 | return ( 103 | 108 | ); 109 | }; 110 | export const FeatureComponent = (props: FeatureComponentProps) => { 111 | const { onChange, feature, onDelete, onEdit, onMoveDown, onMoveUp } = props; 112 | return ( 113 |
114 | 123 |
124 | ); 125 | }; 126 | -------------------------------------------------------------------------------- /src/components/editor/ImageComponent.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import * as styles from "./styles/Image"; 4 | import { translated } from "@/models"; 5 | import { MAX_IMAGE_WIDTH } from "@/constants"; 6 | import { 7 | DeleteAndEditIconsComponentProps, 8 | DeleteAndEditIconsComponent 9 | } from "./display/DeleteAndEdit"; 10 | 11 | import { TopMenuProps, TopMenu } from "@/topmenu/index"; 12 | 13 | const t = translated("ImageComponentTxt"); 14 | 15 | function getBase64(file: Blob): Promise { 16 | return new Promise((resolve, reject) => { 17 | const reader = new FileReader(); 18 | reader.readAsDataURL(file); 19 | reader.onload = () => { 20 | let encoded = reader.result! as string; 21 | resolve(encoded); 22 | }; 23 | reader.onerror = error => reject(error); 24 | }); 25 | } 26 | export interface ImageComponentProps 27 | extends DeleteAndEditIconsComponentProps, 28 | TopMenuProps { 29 | image: PartialObjects["Image"]; 30 | onChange: () => void; 31 | } 32 | 33 | export const ImageComponent = ({ 34 | onChange, 35 | image, 36 | onDelete, 37 | // onEdit, 38 | onMoveDown, 39 | onMoveUp 40 | }: ImageComponentProps) => { 41 | const [imageRef, setImageRef] = useState(); 42 | const [ratio, setRatio] = useState(1.0); 43 | return ( 44 |
45 |
52 |

{t("ImageTitle")}

53 | 54 | 60 |
61 | 62 |
63 | {image.url && ( 64 | { 69 | if (ref) { 70 | setImageRef(ref); 71 | } 72 | }} 73 | /> 74 | )} 75 | {image.base64 && ( 76 | { 81 | if (!image.width || !image.height) { 82 | const ratio = imageRef!.naturalHeight / imageRef!.naturalWidth; 83 | image.width = Math.min(imageRef!.naturalWidth, MAX_IMAGE_WIDTH); 84 | image.height = ratio * image.width; 85 | } 86 | setRatio(image.width / image.height); 87 | onChange(); 88 | }} 89 | ref={ref => { 90 | if (ref) { 91 | setImageRef(ref); 92 | } 93 | }} 94 | /> 95 | )} 96 | { 99 | // TODO: Upload somewhere and respond with link here then setup image.url for now base64 100 | getBase64(e.target.files![0]!).then((value: string) => { 101 | image.base64 = value; 102 | image.width = undefined; 103 | image.height = undefined; 104 | onChange(); 105 | }); 106 | }} 107 | /> 108 | {image.base64 && imageRef && ( 109 | <> 110 | { 117 | const w = parseInt(e.target.value); 118 | image.width = w; 119 | image.height = Math.floor(w * (1 / ratio)); 120 | onChange(); 121 | }} 122 | /> 123 | { 129 | const h = parseInt(e.target.value); 130 | image.height = h; 131 | image.width = Math.floor(h * ratio); 132 | onChange(); 133 | }} 134 | /> 135 | 136 | )} 137 |
138 |
139 | ); 140 | }; 141 | -------------------------------------------------------------------------------- /src/components/editor/ListBlockComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import { FeatureComponent } from "./FeatureComponent"; 4 | import { Controls } from "./Controls"; 5 | import { 6 | Rolloutable, 7 | } from "./display/Rolloutable"; 8 | import { DeleteAndEditIconsComponentProps } from "./display/DeleteAndEdit"; 9 | import { swapInArray } from "@/utils"; 10 | import { translated } from "@/models"; 11 | import { TopMenu } from "@/topmenu/index"; 12 | // import { TextBlockComponentProps } from "livepdf/livecomponents/editor"; 13 | export interface ListBlockComponentProps extends DeleteAndEditIconsComponentProps { 14 | listBlock: PartialObjects["ListBlock"]; 15 | onChange: () => void; 16 | onEdit: (feature: PartialObjects["Feature"]) => void; 17 | components?: PartialObjects["ListBlock"][]; 18 | } 19 | 20 | const t = translated("ListBlockComponentTxt"); 21 | 22 | export const ListBlockComponent = (props: ListBlockComponentProps) => { 23 | const { onChange, onEdit, listBlock } = props; 24 | return ( 25 | 26 | 27 | {listBlock.items && 28 | listBlock.items.map((i, index) => { 29 | return ( 30 | { 36 | listBlock.items!.splice(index, 1); 37 | onChange(); 38 | }} 39 | onMoveDown={ 40 | index !== listBlock.items!.length - 1 41 | ? () => { 42 | swapInArray(listBlock.items!, index, index + 1); 43 | onChange(); 44 | } 45 | : undefined 46 | } 47 | onMoveUp={ 48 | index !== 0 && listBlock.items!.length > 1 49 | ? () => { 50 | swapInArray(listBlock.items!, index, index - 1); 51 | onChange(); 52 | } 53 | : undefined 54 | } 55 | /> 56 | ); 57 | })} 58 | {listBlock.items && ( 59 | { 64 | listBlock.items = []; 65 | onChange(); 66 | }} 67 | /> 68 | )} 69 | 70 | ); 71 | }; 72 | -------------------------------------------------------------------------------- /src/components/editor/StackComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import { FeatureComponent } from "./FeatureComponent"; 4 | import { Controls } from "./Controls"; 5 | import { Rolloutable } from "./display/Rolloutable"; 6 | import { DeleteAndEditIconsComponentProps } from "./display/DeleteAndEdit"; 7 | import { swapInArray } from "@/utils"; 8 | import { translated } from "@/models"; 9 | import { TopMenu } from "@/topmenu/index"; 10 | export interface StackComponentProps extends DeleteAndEditIconsComponentProps { 11 | stack: PartialObjects["Stack"]; 12 | onChange: () => void; 13 | onEdit: (feature: PartialObjects["Feature"]) => void; 14 | components?: PartialObjects["TemplateComponent"][]; 15 | hideControls?: boolean; 16 | topMenuControls?: boolean; 17 | } 18 | 19 | const t = translated("StackComponentTxt"); 20 | 21 | export const StackComponent = (props: StackComponentProps) => { 22 | const { onChange, onEdit, stack, hideControls } = props; 23 | return ( 24 | 25 | 26 | {stack.items && 27 | stack.items.map((i, index) => { 28 | return ( 29 | { 35 | stack.items!.splice(index, 1); 36 | onChange(); 37 | }} 38 | onMoveDown={ 39 | index !== stack.items!.length - 1 40 | ? () => { 41 | swapInArray(stack.items!, index, index + 1); 42 | onChange(); 43 | } 44 | : undefined 45 | } 46 | onMoveUp={ 47 | index !== 0 && stack.items!.length > 1 48 | ? () => { 49 | swapInArray(stack.items!, index, index - 1); 50 | onChange(); 51 | } 52 | : undefined 53 | } 54 | /> 55 | ); 56 | })} 57 | 58 | {stack.items && ( 59 | { 68 | stack.items = []; 69 | onChange(); 70 | }} 71 | /> 72 | )} 73 | 74 | ); 75 | }; 76 | -------------------------------------------------------------------------------- /src/components/editor/TableBlockComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import { ColumnsComponent } from "./ColumnsComponent"; 4 | import { Rolloutable } from "./display/Rolloutable"; 5 | import * as Icons from "react-feather"; 6 | import cx from "classnames"; 7 | import * as styles1 from "./styles/Feature"; 8 | import { translated } from "@/models"; 9 | import * as styles from "./styles/TableBlock"; 10 | import { DeleteAndEditIconsComponentProps } from "./display/DeleteAndEdit"; 11 | import { TopMenu } from "@/topmenu/index"; 12 | 13 | export interface TableBlockComponentProps 14 | extends DeleteAndEditIconsComponentProps { 15 | tableBlock: PartialObjects["TableBlock"]; 16 | onChange: () => void; 17 | onEdit: (feature: PartialObjects["Feature"]) => void; 18 | components?: PartialObjects["TemplateComponent"][]; 19 | } 20 | 21 | const emptyColumn = (): PartialObjects["Column"] => ({ 22 | __typename: "Column", 23 | content: { 24 | __typename: "Stack", 25 | items: [] 26 | } 27 | }); 28 | 29 | const t = translated("TableBlockComponentTxt"); 30 | 31 | const width2Power = (width: string) => parseFloat(width.slice(0, -1)); 32 | 33 | const normalizeWidths = ( 34 | twidths: PartialObjects["WidthType"][] 35 | ): PartialObjects["WidthType"][] => { 36 | const powers = calculatePowers( 37 | twidths.map(tw => ({ 38 | ...tw, 39 | S: tw.S || "*" 40 | })) 41 | ); 42 | return calculateWidthsFromPowers(powers); 43 | }; 44 | const calculatePowers = (twidths: PartialObjects["WidthType"][]) => 45 | twidths.map(w => (w.S! === "*" ? 100 : parseFloat(w.S!.slice(0, -1)))); 46 | const calculateWidthsFromPowers = ( 47 | powers: number[] 48 | ): PartialObjects["WidthType"][] => { 49 | const powerValue = powers.reduce((a, b) => a + b, 0); 50 | return powers.map(p => ({ 51 | S: `${(100.0 * p) / powerValue}%` 52 | })); 53 | }; 54 | 55 | export const TableBlockComponent = (props: TableBlockComponentProps) => { 56 | const { onChange, onEdit, tableBlock } = props; 57 | const widths = tableBlock 58 | .widths!.map(tw => ({ 59 | ...tw, 60 | S: tw.S || "100%" 61 | })) 62 | .map(w => w.S); 63 | return ( 64 | 70 | 71 |
72 |
73 | {tableBlock.rows && 74 | tableBlock.rows.map((i, index) => ( 75 | { 82 | tableBlock.rows = tableBlock.rows!.filter( 83 | (row, rowIndex) => rowIndex !== index 84 | ); 85 | onChange(); 86 | }} 87 | onResize={(columnIndex, percentage) => { 88 | const delta = 89 | width2Power(tableBlock.widths![columnIndex].S!) - 90 | percentage; 91 | tableBlock.widths![columnIndex] = { S: `${percentage}%` }; 92 | if (columnIndex === widths.length - 1) { 93 | tableBlock.widths![columnIndex - 1] = { 94 | S: `${width2Power( 95 | tableBlock.widths![columnIndex - 1].S! 96 | ) + delta}%` 97 | }; 98 | } else { 99 | tableBlock.widths![columnIndex + 1] = { 100 | S: `${width2Power( 101 | tableBlock.widths![columnIndex + 1].S! 102 | ) + delta}%` 103 | }; 104 | } 105 | onChange(); 106 | }} 107 | /> 108 | ))} 109 |
110 |
111 | {tableBlock.rows && 112 | tableBlock.rows[0] && 113 | tableBlock.rows[0].columns!.length > 1 && 114 | tableBlock.rows[0].columns!.map((i, index) => ( 115 |
122 | {/* poprawić paddingi */} 123 | 124 | { 127 | tableBlock.rows = tableBlock.rows!.map(row => ({ 128 | ...row, 129 | columns: row.columns!.filter((_, idx) => idx !== index) 130 | })); 131 | tableBlock.widths! = tableBlock.widths!.filter( 132 | (tw, idx) => idx !== index 133 | ); 134 | tableBlock.widths! = normalizeWidths(tableBlock.widths!); 135 | onChange(); 136 | }} 137 | className={cx(styles1.MiniIcon, styles1.Delete)} 138 | /> 139 | {/* {t("ControleButtonDelete")} */} 140 | {/* { 142 | tableBlock.rows = tableBlock.rows!.map(row => ({ 143 | ...row, 144 | columns: row.columns!.filter((_, idx) => idx !== index) 145 | })); 146 | tableBlock.widths! = tableBlock.widths!.filter( 147 | (tw, idx) => idx !== index 148 | ); 149 | tableBlock.widths! = normalizeWidths(tableBlock.widths!); 150 | onChange(); 151 | }} 152 | > 153 | {TableBlockComponentTxt.ControleButtonDelete} 154 | */} 155 |
156 | ))} 157 |
158 |
159 |
160 |
161 | { 166 | if (tableBlock.widths!.length === 0) { 167 | tableBlock.widths!.push({ 168 | S: "*" 169 | }); 170 | tableBlock.widths! = normalizeWidths(tableBlock.widths!); 171 | } 172 | tableBlock.rows!.push({ 173 | __typename: "Row", 174 | columns: 175 | tableBlock.rows!.length > 0 176 | ? tableBlock.rows![ 177 | tableBlock.rows!.length - 1 178 | ].columns!.map(() => emptyColumn()) 179 | : [emptyColumn()] 180 | }); 181 | onChange(); 182 | }} 183 | > 184 | 185 | 186 |
187 | {tableBlock.rows && tableBlock.rows.length > 0 && ( 188 |
189 | { 194 | tableBlock.rows!.forEach(row => { 195 | row.columns!.push(emptyColumn()); 196 | }); 197 | tableBlock.widths!.push({ 198 | S: "100%" 199 | }); 200 | tableBlock.widths! = normalizeWidths(tableBlock.widths!); 201 | onChange(); 202 | }} 203 | > 204 | 205 | 206 |
207 | )} 208 |
209 |
210 | ); 211 | }; 212 | -------------------------------------------------------------------------------- /src/components/editor/TextBlockComponent.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect } from "react"; 2 | import { PartialObjects } from "@/graphql-zeus"; 3 | import * as styles from "./styles/TextBlock"; 4 | import { 5 | DeleteAndEditIconsComponentProps, 6 | DeleteAndEditIconsComponent 7 | } from "./display/DeleteAndEdit"; 8 | import { translated } from "@/models"; 9 | 10 | import { TopMenuProps, TopMenu } from "@/topmenu/index"; 11 | // import { Controls } from "./Controls"; 12 | 13 | // czy tutaj trzeba zrobić export interface ... extends Delete || TopMenuProps 14 | 15 | // chyba tak 16 | 17 | const t = translated("TextBlockComponentTxt"); 18 | 19 | export interface TextBlockComponentProps 20 | extends DeleteAndEditIconsComponentProps, 21 | TopMenuProps { 22 | textBlock: PartialObjects["TextBlock"]; 23 | onChange: () => void; 24 | } 25 | 26 | const AutoResizeTextArea = ( 27 | props: React.DetailedHTMLProps< 28 | React.TextareaHTMLAttributes, 29 | HTMLTextAreaElement 30 | > 31 | ) => { 32 | const tRef = useRef(null); 33 | // przez useRef jest dostęp do DOMa przeglądarki 34 | 35 | useEffect(() => { 36 | if (tRef.current) { 37 | tRef.current.setAttribute("style", "height: auto;"); 38 | //setAttribute 39 | 40 | tRef.current.setAttribute( 41 | "style", 42 | `height: ${tRef.current.scrollHeight}px;` 43 | ); 44 | } 45 | }, [props.value]); 46 | return