├── .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 | [](https://nodesource.com/products/nsolid)
5 |
6 | [](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 | 
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 |
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 |
186 |
187 | {tableBlock.rows && tableBlock.rows.length > 0 && (
188 |
189 |
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 ;
47 | };
48 | export const TextBlockComponent = (props: TextBlockComponentProps) => {
49 | const { onChange, textBlock } = props;
50 | return (
51 |
52 |
53 |
{t("TextBlockTitle")}
54 |
55 |
56 |
57 |
{
63 | textBlock.text = e.target.value;
64 | onChange();
65 | }}
66 | />
67 |
68 | );
69 | };
70 |
--------------------------------------------------------------------------------
/src/components/editor/TimeStampComponent.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as styles from "./styles/TextBlock";
3 | import { translated } from "@/models";
4 | export interface TimeStampComponentProps { }
5 |
6 |
7 |
8 | export const TimeStampComponent = ({ }: TimeStampComponentProps) => {
9 | const t = translated("TimeStampComponentTxt");
10 | return {`${t("Date")}`}
;
11 | };
12 |
--------------------------------------------------------------------------------
/src/components/editor/display/DeleteAndEdit.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import * as Icons from "react-feather";
4 | import cx from "classnames";
5 | import * as styles1 from "@components/editor/styles/Feature";
6 |
7 | export interface DeleteAndEditIconsComponentProps {
8 | feature?: PartialObjects["Feature"];
9 | onDelete?: () => void;
10 | // onEdit?: (feature: PartialObjects["Feature"]) => void;
11 | isRoot?: boolean;
12 | onMoveDown?: () => void;
13 | onMoveUp?: () => void;
14 | withoutDeleteIcons?: boolean;
15 | }
16 |
17 | export const DeleteAndEditIconsComponent = ({
18 | feature,
19 | onDelete,
20 | isRoot,
21 | // onEdit,
22 | onMoveDown,
23 | onMoveUp
24 | }: DeleteAndEditIconsComponentProps) => (
25 |
26 | {onMoveDown && (
27 |
onMoveDown()}
30 | >
31 |
37 |
38 | )}
39 | {onMoveUp && (
40 |
onMoveUp()}
43 | >
44 |
50 |
51 | )}
52 | {!isRoot && (
53 |
54 |
60 |
61 | )}
62 | {/*
63 | onEdit && feature && onEdit(feature)}
69 | size={15}
70 | />
71 |
*/}
72 |
73 | );
74 |
75 |
--------------------------------------------------------------------------------
/src/components/editor/display/Rolloutable.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as styles from "./styles/Rolloutable";
3 | import * as icons from "react-feather";
4 | import { PartialObjects } from "@/graphql-zeus";
5 | import {
6 | DeleteAndEditIconsComponentProps,
7 | DeleteAndEditIconsComponent
8 | } from "./DeleteAndEdit";
9 |
10 | export interface RolloutableComponentProps
11 | extends DeleteAndEditIconsComponentProps {
12 | children: React.ReactNode;
13 | title: string;
14 | feature?: PartialObjects["Feature"];
15 | onEdit?: (feature: PartialObjects["Feature"]) => void;
16 | }
17 | interface RolloutableState {
18 | rolledOut: boolean;
19 | }
20 |
21 | export class Rolloutable extends React.Component<
22 | RolloutableComponentProps,
23 | RolloutableState
24 | > {
25 | state: RolloutableState = {
26 | rolledOut: true
27 | };
28 | render() {
29 | return (
30 |
31 |
32 | {this.state.rolledOut ? (
33 |
{
36 | this.setState({ rolledOut: !this.state.rolledOut });
37 | }}
38 | />
39 | ) : (
40 | {
43 | this.setState({ rolledOut: !this.state.rolledOut });
44 | }}
45 | />
46 | )}
47 |
48 | {
50 | this.setState({ rolledOut: !this.state.rolledOut });
51 | }}
52 | >
53 | {this.props.title}
54 |
55 | {!this.props.withoutDeleteIcons && (
56 |
57 | )}
58 |
59 |
63 | {this.props.children}
64 |
65 |
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/editor/display/SectionTitle.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as styles from "./styles/SectionTitle";
3 | export const SectionTitle = (props: { children: React.ReactNode }) => (
4 | {props.children}
5 | );
6 |
--------------------------------------------------------------------------------
/src/components/editor/display/TopMenuEdit.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import * as Icons from "react-feather";
4 | import cx from "classnames";
5 | import * as styles1 from "@components/editor/styles/Feature";
6 |
7 | export interface DeleteAndEditIconsComponentProps {
8 | feature?: PartialObjects["Feature"];
9 | onDelete?: () => void;
10 | onEdit?: (feature: PartialObjects["Feature"]) => void;
11 | isRoot?: boolean;
12 | onMoveDown?: () => void;
13 | onMoveUp?: () => void;
14 | withoutDeleteIcons?: boolean;
15 | }
16 |
17 | export const DeleteAndEditIconsComponent = ({
18 | feature,
19 | onDelete,
20 | isRoot,
21 | onEdit,
22 | onMoveDown,
23 | onMoveUp
24 | }: DeleteAndEditIconsComponentProps) => (
25 |
26 | {onMoveDown && (
27 |
onMoveDown()}
30 | >
31 |
37 |
38 | )}
39 | {onMoveUp && (
40 |
onMoveUp()}
43 | >
44 |
50 |
51 | )}
52 | {!isRoot && (
53 |
54 |
60 |
61 | )}
62 |
63 | onEdit && feature && onEdit(feature)}
69 | size={15}
70 | />
71 |
72 |
73 | );
74 |
75 |
--------------------------------------------------------------------------------
/src/components/editor/display/styles/Rolloutable.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 | import { transition } from "@/constants";
4 | export const Title = style({
5 | $debugName: "RolloutableTitle",
6 | cursor: "pointer",
7 | fontWeight: "bold",
8 | color: Colors["Black Hole"],
9 | fontSize: 12,
10 | marginBottom: 10,
11 | display: "flex",
12 | alignItems: "center"
13 | });
14 | export const RolloutableChildren = style({
15 | $debugName: "RolloutableChildren",
16 |
17 | });
18 |
19 | export const RolloutableMain = style({
20 | $debugName: "RolloutableMain",
21 | padding: `5px 10px 5px`,
22 | flex: 1,
23 | borderRadius: 10,
24 | transition: transition,
25 | marginBottom: 10,
26 | border: `1px dashed ${Colors.Foggy}`
27 | // border: `2px dashed red`
28 | });
29 |
--------------------------------------------------------------------------------
/src/components/editor/display/styles/SectionTitle.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 |
3 | export const Main = style({
4 | $debugName: "SectionTitleMain",
5 | fontWeight: "bold",
6 | padding: 5
7 | });
8 |
--------------------------------------------------------------------------------
/src/components/editor/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ColumnsComponent";
2 | export * from "./FeatureComponent";
3 | export * from "./ImageComponent";
4 | export * from "./ListBlockComponent";
5 | export * from "./StackComponent";
6 | export * from "./TableBlockComponent";
7 | export * from "./TextBlockComponent";
8 | export * from "./TimeStampComponent";
9 | export * from "./Controls";
10 | export * from "./DocumentComponent";
11 | export * from "./ColumnComponent";
12 | export * from "./EmptyFeatureComponent";
13 |
--------------------------------------------------------------------------------
/src/components/editor/styles/Column.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 |
3 | export const Main = style({
4 | });
5 |
--------------------------------------------------------------------------------
/src/components/editor/styles/Columns.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 |
4 | export const Main = style({
5 | $debugName: "ColumnsMain",
6 | display: "flex",
7 | flexFlow: "row nowrap",
8 | borderBottom: `1px solid ${Colors.Ashes}77`,
9 | width: "100%"
10 | });
11 |
12 | export const ColumnTitleDiv = style({
13 | $debugName: "ColumnTitleDiv",
14 | display: "flex",
15 | alignItems: "center",
16 | marginTop: 5,
17 | marginBottom: 5
18 | })
19 |
20 | export const ColumnTitle = style({
21 | $debugName: "ColumnTitle",
22 | fontSize: 12,
23 | margin: 0,
24 | fontWeight: 'bold'
25 | })
26 |
--------------------------------------------------------------------------------
/src/components/editor/styles/Controls.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 | import { transition } from "@/constants";
4 |
5 | export const Main = style({
6 | $debugName: "ControlsMain",
7 | display: "flex",
8 | justifyContent: "flex-start",
9 | borderRadius: 2,
10 | alignItems: "start",
11 | position: "relative"
12 | });
13 | export const Category = style({
14 | $debugName: "ControlsCategory",
15 | display: "flex",
16 | justifyContent: "flex-start",
17 | borderRadius: 2,
18 | flexWrap: "wrap"
19 | });
20 |
21 | export const Button = style({
22 | $debugName: "ControlsButton",
23 | fontSize: 10,
24 | padding: `5px 12px`,
25 | background: Colors.White,
26 | borderRadius: 2,
27 | cursor: "pointer",
28 | margin: `0 5px 5px 0`,
29 | border: "1px solid black",
30 | transition,
31 | $nest: {
32 | "&:hover": {
33 | background: Colors["Outer Space"],
34 | color: Colors["White"]
35 | }
36 | }
37 | });
38 | export const MenuHeader = style({
39 | $debugName: "ControlsMenuHandler",
40 | fontSize: 10,
41 | fontWeight: "bold",
42 | color: Colors.Ashes,
43 | marginTop: 10
44 | });
45 | export const Menu = style({
46 | $debugName: "ControlsMenu",
47 | padding: 20,
48 | width: 300,
49 | borderRadius: 4,
50 | boxShadow: `#000a 0px 3px 10px`,
51 | background: Colors.White,
52 | zIndex: 4
53 | });
54 | export const Overlay = style({
55 | $debugName: "ControlsOverlay",
56 | // position: "fixed",
57 | top: 0,
58 | left: 0,
59 | width: "100%",
60 | height: "100%",
61 | display: "flex",
62 | alignItems: "start",
63 | justifyContent: "left",
64 | // background: `${Colors.Black}aa`,
65 | background: '#f9f9f9',
66 | zIndex: 3,
67 | paddingLeft: '5%'
68 | // padding: "15%"
69 | });
70 | export const PlusMinus = style({
71 | $debugName: "ControlsPlusMinus",
72 | color: Colors["Ancient Stone"],
73 | padding: 5,
74 | cursor: "pointer",
75 | $nest: {
76 | "&:hover": {
77 | color: Colors.Cooler
78 | }
79 | }
80 | });
81 |
--------------------------------------------------------------------------------
/src/components/editor/styles/Feature.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 |
4 | export const FeatureMain = style({
5 | $debugName: "FeatureMain",
6 | marginBottom: 10,
7 | display: "flex"
8 | // border: '2px dashed green'
9 | });
10 | export const FeatureOptions = style({
11 | $debugName: "FeatureOptions",
12 | display: "flex",
13 | flexFlow: "row nowrap",
14 | flexDirection: "row-reverse",
15 | width: "100%"
16 | });
17 | export const MiniIcon = style({
18 | $debugName: "FeatureMiniIcon",
19 | cursor: "pointer",
20 | alignSelf: "flex-start",
21 | marginRight: 5,
22 | color: Colors.Androgyn
23 |
24 | });
25 |
26 | export const Delete = style({
27 | $debugName: "FeatureDelete",
28 | $nest: {
29 | "&:hover": {
30 | color: Colors["Cherry Bomb"]
31 | }
32 | }
33 | });
34 |
35 | export const Edit = style({
36 | $debugName: "FeatureEdit",
37 | $nest: {
38 | "&:hover": {
39 | color: Colors["Super Nova"]
40 | }
41 | }
42 | });
43 |
44 | export const FeatureTitleDiv = style({
45 | $debugName: "FeatureTitleDiv",
46 | display: "flex",
47 | justifyContent: "space-between"
48 | });
49 |
50 | export const Basket = style({
51 | $debugName: "Basket",
52 | display: "flex",
53 | marginTop: 5,
54 | marginLeft: 5,
55 | marginBottom: 10
56 | });
57 |
--------------------------------------------------------------------------------
/src/components/editor/styles/Image.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 |
3 | export const ImageWrap = style({
4 | $debugName: "ImageWrap",
5 | display: "flex",
6 | marginTop: 5,
7 | flexFlow: "column nowrap"
8 | });
9 | export const ImageMain = style({
10 | $debugName: "ImageMain",
11 | width: 30,
12 | height: "auto",
13 | marginRight: 10,
14 | maxWidth: 100
15 | });
16 |
17 | export const ImageTitle = style({
18 | $debugName: "ImageTitle",
19 | fontSize: 12,
20 | margin: 0,
21 | fontWeight: "bold"
22 | });
23 |
24 | export const ImageTitleDiv = style({
25 | $debugName: "ImageTitleDiv",
26 | display: "flex",
27 | justifyContent: "center"
28 | });
29 |
--------------------------------------------------------------------------------
/src/components/editor/styles/ListBlock.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 |
3 | export const Main = style({
4 | $debugName:"ListBlockMain",
5 | padding: 10,
6 | marginBottom: 25
7 | });
8 |
--------------------------------------------------------------------------------
/src/components/editor/styles/Stack.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@components/editor/styles/Colors";
3 |
4 | export const Main = style({
5 | $debugName: "StackMain",
6 | padding: 10,
7 | border: `2px solid ${Colors["Bored Martian"]}`,
8 | borderTopWidth: 0,
9 | borderRightWidth: 0,
10 | marginBottom: 25
11 | });
12 | export const Title = style({
13 | $debugName: "StackTitle",
14 | cursor: "pointer",
15 | fontWeight: "bold"
16 | });
17 |
--------------------------------------------------------------------------------
/src/components/editor/styles/TableBlock.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | export const Actions = style({
3 | $debugName: "TableBlockActions",
4 | display: "flex",
5 | flexFlow: "row wrap",
6 | marginTop: 5
7 | });
8 |
9 | export const ControlAddRow = style ({
10 | $debugName: "ControlAddRow",
11 | height: 24,
12 | width: 24,
13 | cursor: "pointer",
14 | $nest: {
15 | "&:hover": {
16 | fill: '#56DA67',
17 | }
18 | }
19 | })
20 | export const ControlAddColumn = style ({
21 | $debugName: "ControlAddColumn",
22 | height: 24,
23 | width: 24,
24 | cursor: "pointer",
25 | $nest: {
26 | "&:hover": {
27 | fill: '#56DA67',
28 | }
29 | }
30 | })
31 | export const ControlColumns = style({
32 | $debugName: "TableBlockControlColums",
33 | display: "flex",
34 | flexFlow: "nowrap"
35 | });
36 | export const ControlCell = style({
37 | $debugName: "TableBlockControlCell",
38 | padding: 10,
39 | $nest: {
40 | input: {
41 | width: "100%"
42 | }
43 | }
44 | });
45 |
46 | export const TableBlockSvg = style({
47 | $debugName: "TableBlockSvg",
48 | display: "flex",
49 | alignItems: "center",
50 | marginRight: 10
51 | })
52 |
--------------------------------------------------------------------------------
/src/components/editor/styles/TextBlock.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 |
4 | export const Main = style({
5 | $debugName: "TextBlockMain",
6 | display: "flex",
7 | // border: 0,
8 | marginTop: 5,
9 | padding: 10,
10 | width: "100%",
11 | background: Colors.White,
12 | fontFamily: "Fira Sans",
13 | border: "1px solid #DFE6F7",
14 | borderRadius: 5
15 | });
16 | export const styleLabel = style({
17 | $debugName: "TextBlockStyleLabel",
18 | fontSize: 10,
19 | color: Colors.Black,
20 | padding: 5,
21 | textAlign: "center",
22 | cursor: "pointer"
23 | });
24 |
25 | export const TextBlockTitle = style({
26 | $debugName: "TextBlockTitle",
27 | fontSize: 12,
28 | margin: 0,
29 | fontWeight: "bold"
30 | });
31 |
32 | export const TextBlockSvg = style({
33 | $debugName: "TextBlockSvg",
34 | display: "flex",
35 | alignItems: "center",
36 | marginRight: 10
37 | });
38 |
39 | export const TextBlockTitleDiv = style({
40 | $debugName: "TextBlockTitleDiv",
41 | display: "flex",
42 | justifyContent: "center"
43 | });
44 |
45 | export const TextBlockContainer = style({
46 | $debugName: "TextBlockContainer",
47 | width: "100%"
48 | })
49 |
--------------------------------------------------------------------------------
/src/components/icons/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Colors } from "@/Colors";
3 |
4 | interface IIconFill {
5 | fill: keyof typeof Colors;
6 | }
7 |
8 | export const CheckBox = ({ fill }: IIconFill) => (
9 |
23 | );
24 |
25 | export const LogoDark = () => (
26 |
53 | );
54 | export const Logo = () => (
55 |
82 | );
83 |
84 | export const addColumn = () => (
85 |
95 | );
96 |
97 | export const addBlock = () => (
98 |
108 | );
109 |
--------------------------------------------------------------------------------
/src/components/index.tsx:
--------------------------------------------------------------------------------
1 | import * as Icons from "./icons";
2 | import * as Editor from "./editor";
3 | export { Icons, Editor };
4 |
--------------------------------------------------------------------------------
/src/components/molecules/Confirm.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { ButtonSimple } from "@components/atoms";
3 | import { translated } from "@/models";
4 |
5 | export interface IConfirmProps {
6 | children: React.ReactNode;
7 | onConfirm: () => void;
8 | style?: React.CSSProperties;
9 | }
10 |
11 | export const Confirm = ({ children, onConfirm }: IConfirmProps) => {
12 | const [del, setDel] = useState(false);
13 | const t = translated("ConfirmTxt");
14 | return (
15 | <>
16 | {!del && (
17 | setDel(true)}
23 | >
24 | {children}
25 |
26 | )}
27 | {del && (
28 | <>
29 |
34 | {t("AreYouSure")}
35 |
36 |
37 | {
39 | setDel(false);
40 | onConfirm();
41 | }}
42 | >
43 | {t("Yes")}
44 |
45 |
46 | setDel(false)}>{t("No")}
47 | >
48 | )}
49 | >
50 | );
51 | };
52 |
--------------------------------------------------------------------------------
/src/components/styles/ButtonSimple.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 | import { transition } from "@/constants";
4 | export const Main = style({
5 | $debugName: "ButtonSimpleMain",
6 | display: "inline-block",
7 | fontSize: 12,
8 | color: Colors["Outer Space"],
9 | padding: `5px 12px`,
10 | border: `1px solid`,
11 | borderRadius: 3,
12 | cursor: "pointer",
13 | transition,
14 | $nest: {
15 | "&:hover": {
16 | background: Colors["Outer Space"],
17 | color: Colors["White"],
18 | border: `1px solid`,
19 | borderRadius: 3
20 | }
21 | }
22 | });
23 |
--------------------------------------------------------------------------------
/src/components/styles/CheckboxStyles.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 |
4 | export const Checkbox = style({
5 | $debugName: "Checkbox",
6 | // height: 20,
7 | // width: 60,
8 | display: "flex",
9 | alignItems: "center"
10 | });
11 |
12 | export const CheckboxParagraph = style({
13 | $debugName: "CheckboxParagraph",
14 | fontSize: 20,
15 | marginTop: 0,
16 | marginBottom: 0
17 | });
18 |
19 | export const CheckboxElement = style({
20 | $debugName: "CheckboxElement",
21 | width: 20,
22 | height: 20,
23 | display: "flex",
24 | flex: "none",
25 | cursor: "pointer",
26 | marginRight: 17,
27 | $nest: {
28 | "&.on": {
29 | border: `2px solid ${Colors.Sopel}`,
30 | backgroundColor: Colors.White,
31 | borderRadius: "4px 4px",
32 | $nest: {
33 | "&.round": {
34 | borderRadius: "13px 13px",
35 | $nest: {
36 | svg: {
37 | display: "none",
38 | maxHeight: "100%",
39 | maxWidth: "100%"
40 | }
41 | }
42 | },
43 | // prettier-ignore
44 | 'svg': {
45 | display: 'none',
46 | maxHeight: '100%',
47 | maxWidth: '100%'
48 | },
49 | "&.active": {
50 | border: `2px solid ${Colors["Super Nova"]}`,
51 | backgroundColor: Colors.White,
52 | $nest: {
53 | svg: {
54 | display: "block",
55 | marginLeft: 1,
56 | marginTop: 2,
57 | marginRight: 2
58 | }
59 | }
60 | }
61 | }
62 | }
63 | }
64 | });
65 |
--------------------------------------------------------------------------------
/src/components/styles/IconButton.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 | import { transition } from "@/constants";
4 | export const Main = style({
5 | $debugName: "IconButtonMain",
6 | display: "flex",
7 | alignItems: "center",
8 | justifyContent: "center",
9 | color: Colors["Ancient Stone"],
10 | background: Colors.White,
11 | padding: `4px 8px`,
12 | borderRadius: 3,
13 | cursor: "pointer",
14 | transition,
15 | $nest: {
16 | "&:hover": {
17 | background: Colors["Outer Space"],
18 | color: Colors["White"]
19 | },
20 | span: {
21 | fontSize: 10,
22 | marginRight: 5,
23 | textTransform: "uppercase"
24 | }
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/src/components/styles/TopMenu.tsx:
--------------------------------------------------------------------------------
1 | import { style, classes } from "typestyle";
2 | import { Colors } from "@/Colors";
3 | import { TOPHEIGHT } from "@/constants";
4 | export const Main = style({
5 | $debugName: "TopMenuMain",
6 | fontFamily: "Fira Sans",
7 | background: Colors.White,
8 | display: "flex",
9 | flexWrap: "wrap",
10 | maxHeight: '100%',
11 | minHeight: TOPHEIGHT,
12 | });
13 | export const Placement = style({
14 | $debugName: "TopMenuPlacement",
15 | padding: 5,
16 | display: "flex",
17 | alignItems: "center"
18 | });
19 |
20 | export const Button = classes(
21 | Placement,
22 | style({
23 | $debugName: "TopMenuButton",
24 | cursor: "pointer",
25 | color: Colors["Androgyn"],
26 | $nest: {
27 | "&:hover,&.active": {
28 | color: Colors["Rising Embers"]
29 | }
30 | }
31 | })
32 | );
33 | export const Input = style({
34 | $debugName: "TopMenuInput",
35 | width: 40
36 | });
37 | export const ColorPicker = style({
38 | $debugName: "TopMenuColorPicker",
39 | marginTop: 10
40 | });
41 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | export const containerWidth = "80%";
3 | export const maxWidth = 1170;
4 |
5 | export const defaultFont = 16;
6 | export const smallFont = 12;
7 | export const largeFont = 22;
8 |
9 | export const transition = "0.3s ease-out";
10 |
11 | export const MAX_IMAGE_WIDTH = 350;
12 |
13 | export const DefaultValues: ReactPDF.Style = {
14 | fontFamily: "Fira Sans",
15 | marginBottom: 0,
16 | marginLeft: 0,
17 | marginRight: 0,
18 | marginTop: 0,
19 | paddingTop: 0,
20 | paddingBottom: 0,
21 | paddingLeft: 0,
22 | paddingRight: 0,
23 | borderTopWidth: 0,
24 | borderRightWidth: 0,
25 | borderBottomWidth: 0,
26 | borderLeftWidth: 0,
27 | fontSize: 10
28 | };
29 |
30 | export const TOPHEIGHT = 40;
--------------------------------------------------------------------------------
/src/frontend-types.ts:
--------------------------------------------------------------------------------
1 | import { PartialObjects } from "./graphql-zeus";
2 |
3 | export type FrontendTypes = {
4 | ["TemplateComponent"]: Omit & {
5 | feature: PartialObjects["Feature"]
6 | },
7 | ["Document"]: Omit & {
8 | features?: FrontendTypes["Features"]
9 | }
10 | ["Features"]: Omit & {
11 | items?: PartialObjects["Feature"][]
12 | }
13 | ["MachineDocument"]: Omit & {
14 | footer?: PartialObjects["Feature"],
15 | header?: PartialObjects["Feature"],
16 | documents: FrontendTypes["Document"][]
17 | },
18 | ["MachineTemplate"]: Omit & {
19 | template: FrontendTypes["MachineDocument"]
20 | }
21 | }
22 |
23 | export type PickFromMachineTemplate = Pick
24 | export type PickFromComponents = Pick
25 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./screens";
2 |
--------------------------------------------------------------------------------
/src/livepdf/PDFDocument.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Document, Page, View, StyleSheet, Font } from "@react-pdf/renderer";
3 | import { FeatureComponent } from "./livecomponents/editor";
4 | import { FrontendTypes } from "@/frontend-types";
5 | import { DefaultValues } from "@/constants";
6 | export interface PDFDocumentProps {
7 | machine: FrontendTypes["MachineTemplate"];
8 | onRender: () => void;
9 | }
10 |
11 | export const PDFDocument = (props: PDFDocumentProps) => {
12 | const pages =
13 | (props.machine.template && props.machine.template.documents) || [];
14 | const v = props.machine.version || new Date().toISOString();
15 | const version = `${v.slice(0, 10)}`;
16 | return (
17 |
18 | {pages.map((page, pagind) => (
19 |
20 |
21 | {props.machine.template!.header && (
22 |
30 | )}
31 |
32 | {page.features &&
33 | page.features.items &&
34 | page.features.items.map((item, index) => (
35 |
40 | ))}
41 |
50 | {props.machine.template!.footer && (
51 |
55 | )}
56 |
57 |
58 | ))}
59 |
60 | );
61 | };
62 | // Frontend Fonts
63 |
64 |
65 | export const fonts = [
66 | {
67 | family: "Fira Sans",
68 | fonts: [
69 | {
70 | src: require("./fonts/FiraSans-Regular.ttf"),
71 | fontStyle: "normal",
72 | fontWeight: "normal"
73 | },
74 | {
75 | src: require("./fonts/FiraSans-Bold.ttf"),
76 | fontStyle: "normal",
77 | fontWeight: "bold"
78 | }
79 | ]
80 | }
81 | ]
82 | fonts.forEach(f => Font.register(f));
83 |
84 |
85 | const styles = StyleSheet.create({
86 | body: {
87 | paddingTop: 42,
88 | paddingBottom: 30,
89 | paddingHorizontal: 37,
90 | letterSpacing: 0,
91 | fontSize: DefaultValues.fontSize,
92 | fontFamily: "Fira Sans"
93 | },
94 | title: {
95 | textAlign: "center"
96 | },
97 | author: {
98 | fontSize: 12,
99 | textAlign: "center",
100 | marginBottom: 40
101 | },
102 | subtitle: {
103 | fontSize: 18,
104 | margin: 12
105 | },
106 | text: {
107 | margin: 12,
108 | textAlign: "justify"
109 | },
110 | image: {
111 | marginVertical: 15,
112 | marginHorizontal: 100
113 | },
114 | header: {
115 | marginBottom: 20,
116 | textAlign: "center",
117 | color: "grey"
118 | },
119 | pageNumber: {
120 | position: "absolute",
121 | fontSize: 12,
122 | bottom: 30,
123 | left: 0,
124 | right: 0,
125 | textAlign: "center",
126 | color: "grey"
127 | }
128 | });
129 |
--------------------------------------------------------------------------------
/src/livepdf/fonts/FiraSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/33dc039c17d9fd5f911fa2f874f4afa5825809b1/src/livepdf/fonts/FiraSans-Bold.ttf
--------------------------------------------------------------------------------
/src/livepdf/fonts/FiraSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/33dc039c17d9fd5f911fa2f874f4afa5825809b1/src/livepdf/fonts/FiraSans-Light.ttf
--------------------------------------------------------------------------------
/src/livepdf/fonts/FiraSans-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/33dc039c17d9fd5f911fa2f874f4afa5825809b1/src/livepdf/fonts/FiraSans-Medium.ttf
--------------------------------------------------------------------------------
/src/livepdf/fonts/FiraSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/33dc039c17d9fd5f911fa2f874f4afa5825809b1/src/livepdf/fonts/FiraSans-Regular.ttf
--------------------------------------------------------------------------------
/src/livepdf/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | PDFViewer,
4 | } from "@react-pdf/renderer";
5 | import * as stylesMain from "./styles";
6 | import { PDFDocument } from "./PDFDocument";
7 | import { FrontendTypes } from "@/frontend-types";
8 | export interface LivePDFProps {
9 | machine: FrontendTypes["MachineTemplate"]
10 | }
11 | let LOADING = true;
12 |
13 | export class LivePDF extends React.Component {
14 | shouldComponentUpdate() {
15 | return !LOADING;
16 | }
17 | componentDidMount() {
18 | setTimeout(() => {
19 | LOADING = false;
20 | this.forceUpdate();
21 | }, 2000);
22 | }
23 | componentDidUpdate() {
24 | if (
25 | this.props.machine &&
26 | this.props.machine.template &&
27 | this.props.machine.template.documents &&
28 | this.props.machine.template.documents.length > 0 &&
29 | !LOADING
30 | ) {
31 | LOADING = true;
32 | setTimeout(() => {
33 | LOADING = false;
34 | this.forceUpdate();
35 | }, 5000);
36 | }
37 | }
38 | render() {
39 | const { props } = this;
40 | const pages =
41 | (props.machine.template && props.machine.template.documents) || [];
42 | return (
43 | <>
44 | {pages.length > 0 && (
45 |
46 | {
49 | LOADING = false;
50 | }}
51 | />
52 |
53 | )}
54 | >
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/ColumnComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import { FeatureComponent } from "./FeatureComponent";
4 | import ReactPDF, { View } from "@react-pdf/renderer";
5 | export interface ColumnComponentProps {
6 | column: PartialObjects["Column"];
7 | flexBasis: string;
8 | border?: boolean;
9 | version: string;
10 | }
11 |
12 | export const ColumnComponent = ({
13 | column,
14 | flexBasis,
15 | border,
16 | version
17 | }: ColumnComponentProps) => {
18 | const styleJSON: Partial = column.styleJson
19 | ? JSON.parse(column.styleJson)
20 | : {};
21 | const { ...extractStyle } = styleJSON;
22 | let style: ReactPDF.Style = {
23 | ...extractStyle,
24 | width: flexBasis === "*" ? "auto" : flexBasis,
25 | textAlign: "left"
26 | };
27 | if (border) {
28 | style = {
29 | ...style,
30 | borderRightColor: `#444444`,
31 | borderRightWidth: 0.25,
32 | borderRightStyle: `solid`,
33 | display: "flex",
34 | paddingVertical: 5,
35 | paddingHorizontal: 6
36 | };
37 | }
38 | return (
39 |
40 | {column.content && Object.keys(column.content).length > 0 && (
41 |
42 | )}
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/ColumnsComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import { ColumnComponent } from "./ColumnComponent";
4 | import ReactPDF, { View } from "@react-pdf/renderer";
5 | export interface ColumnsComponentProps {
6 | columns: PartialObjects["Columns"];
7 | widths: string[];
8 | border?: boolean;
9 | version: string;
10 | }
11 |
12 | export const ColumnsComponent = ({
13 | columns,
14 | widths,
15 | border,
16 | version
17 | }: ColumnsComponentProps) => {
18 | const styleJSON = columns.styleJson ? JSON.parse(columns.styleJson) : {};
19 | let style: ReactPDF.Style = {
20 | ...styleJSON,
21 | display: "flex",
22 | flexDirection: "row",
23 | alignItems: "stretch",
24 | alignContent: "stretch"
25 | };
26 | if (border) {
27 | style = {
28 | ...style,
29 | borderBottomColor: `#444444`,
30 | borderBottomWidth: 0.25,
31 | borderBottomStyle: `solid`
32 | };
33 | }
34 | return (
35 |
36 | {columns.columns!.map((c, i) => {
37 | const flexBasis = widths[i]!;
38 | return (
39 |
46 | );
47 | })}
48 |
49 | );
50 | };
51 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/FeatureComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import { TextBlockComponent } from "./TextBlockComponent";
4 | import { ImageComponent } from "./ImageComponent";
5 | import { StackComponent } from "./StackComponent";
6 | import { TableBlockComponent } from "./TableBlockComponent";
7 | import { ListBlockComponent } from "./ListBlockComponent";
8 | import { TimeStampComponent } from "./TimeStampComponent";
9 |
10 | export interface FeatureComponentProps {
11 | feature: PartialObjects["Feature"];
12 | version: string;
13 | [k: string]: unknown;
14 | }
15 |
16 | const FeatureComp = (props: FeatureComponentProps) => {
17 | const { feature, version } = props;
18 | if (isTimeStamp(feature)) {
19 | return ;
20 | }
21 | if (isTextBlock(feature)) {
22 | return ;
23 | }
24 | if (isImage(feature)) {
25 | return ;
26 | }
27 | if (isStack(feature)) {
28 | return ;
29 | }
30 | if (isTableBlock(feature)) {
31 | return ;
32 | }
33 | if (isListBlock(feature)) {
34 | return ;
35 | }
36 | return ;
37 | };
38 | export const FeatureComponent = (props: FeatureComponentProps) => {
39 | return ;
40 | };
41 |
42 | export const isTimeStamp = (arg: any): arg is PartialObjects["TimeStamp"] => {
43 | return arg.__typename === "TimeStamp";
44 | };
45 | export const isTextBlock = (arg: any): arg is PartialObjects["TextBlock"] => {
46 | return arg.__typename === "TextBlock";
47 | };
48 | export const isColumns = (arg: any): arg is PartialObjects["Columns"] => {
49 | return arg.__typename === "Columns";
50 | };
51 | export const isColumn = (arg: any): arg is PartialObjects["Column"] => {
52 | return arg.__typename === "Column";
53 | };
54 | export const isStack = (arg: any): arg is PartialObjects["Stack"] => {
55 | return arg.__typename === "Stack";
56 | };
57 | export const isListBlock = (arg: any): arg is PartialObjects["ListBlock"] => {
58 | return arg.__typename === "ListBlock";
59 | };
60 | export const isTableBlock = (arg: any): arg is PartialObjects["TableBlock"] => {
61 | return arg.__typename === "TableBlock";
62 | };
63 | export const isImage = (arg: any): arg is PartialObjects["Image"] => {
64 | return arg.__typename === "Image";
65 | };
66 | export const isFeature = (arg: any): arg is PartialObjects["Feature"] => {
67 | return !!arg.__typename;
68 | };
69 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/ImageComponent.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import ReactPDF, { Image as ReactImage } from "@react-pdf/renderer";
4 |
5 | export interface ImageComponentProps {
6 | image: PartialObjects["Image"];
7 | }
8 |
9 | export const ImageComponent = ({ image }: ImageComponentProps) => {
10 | if (!image.base64 || !image.width || !image.height) {
11 | return <>>;
12 | }
13 | const imageStyle = image.styleJson ? JSON.parse(image.styleJson) : {};
14 | let style: ReactPDF.Style = {
15 | ...imageStyle
16 | };
17 | if (image.width) {
18 | style = {
19 | ...style,
20 | width: image.width
21 | };
22 | }
23 | if (image.height) {
24 | style = {
25 | ...style,
26 | height: image.height
27 | };
28 | }
29 | return ;
30 | };
31 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/ListBlockComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import { FeatureComponent } from "./FeatureComponent";
4 | import { View, Text } from "@react-pdf/renderer";
5 | export interface ListBlockComponentProps {
6 | listBlock: PartialObjects["ListBlock"];
7 | version: string;
8 | }
9 |
10 | export const ListBlockComponent = ({
11 | listBlock,
12 | version
13 | }: ListBlockComponentProps) => {
14 | if (!listBlock.items || listBlock.items.length === 0) {
15 | return <>>;
16 | }
17 |
18 | const style = listBlock.styleJson ? JSON.parse(listBlock.styleJson) : {};
19 | return (
20 |
21 | {listBlock.items &&
22 | listBlock.items.map((i, index) => (
23 |
32 |
37 | •
38 |
39 |
40 |
41 | ))}
42 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/StackComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import { FeatureComponent } from "./FeatureComponent";
4 | import { View } from "@react-pdf/renderer";
5 | export interface StackComponentProps {
6 | stack: PartialObjects["Stack"];
7 | version: string;
8 | [k: string]: unknown;
9 | }
10 |
11 | export const StackComponent = (props: StackComponentProps) => {
12 | const { stack, version } = props;
13 | if (!stack.items || stack.items.length === 0) {
14 | return <>>;
15 | }
16 | const style = stack.styleJson ? JSON.parse(stack.styleJson) : {};
17 | return (
18 |
19 | {stack.items.map((i, index) => (
20 |
21 | ))}
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/TableBlockComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { BuiltInStyles, PartialObjects } from "@/graphql-zeus";
3 | import { ColumnsComponent } from "./ColumnsComponent";
4 | import ReactPDF, { View } from "@react-pdf/renderer";
5 |
6 | export interface TableBlockComponentProps {
7 | tableBlock: PartialObjects["TableBlock"];
8 | version: string;
9 | }
10 |
11 | export const TableBlockComponent = ({
12 | tableBlock,
13 | version
14 | }: TableBlockComponentProps) => {
15 | let style: ReactPDF.Style = tableBlock.styleJson
16 | ? JSON.parse(tableBlock.styleJson)
17 | : {};
18 | const border = tableBlock.style === BuiltInStyles.LIGHT_BORDER_TABLE;
19 | if (border) {
20 | style = {
21 | ...style,
22 | borderColor: "#444444",
23 | borderStyle: "solid",
24 | borderLeftWidth: 0.25
25 | };
26 | }
27 | return (
28 |
29 | {tableBlock.rows &&
30 | tableBlock.rows.map((i, index) => (
31 | w.S!)}
36 | border={border}
37 | />
38 | ))}
39 |
40 | );
41 | };
42 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/TextBlockComponent.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import { Text } from "@react-pdf/renderer";
4 | export interface TextBlockComponentProps {
5 | textBlock: PartialObjects["TextBlock"]
6 | }
7 |
8 | export const TextBlockComponent = ({ textBlock }: TextBlockComponentProps) => {
9 | if (!textBlock.text) {
10 | return <>>;
11 | }
12 | const baseStyle = {};
13 | const style = textBlock.styleJson ? JSON.parse(textBlock.styleJson) : {};
14 | return (
15 |
21 | {textBlock.text}
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/TimeStampComponent.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import { Text } from "@react-pdf/renderer";
4 | export interface TimeStampComponentProps {
5 | timeStamp: PartialObjects["TimeStamp"];
6 | version: string;
7 | }
8 |
9 | export const TimeStampComponent = ({
10 | timeStamp,
11 | version
12 | }: TimeStampComponentProps) => {
13 | const baseStyle = {};
14 | const style = timeStamp.styleJson ? JSON.parse(timeStamp.styleJson) : {};
15 | return (
16 |
22 | {version}
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/src/livepdf/livecomponents/editor/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ColumnsComponent";
2 | export * from "./FeatureComponent";
3 | export * from "./ImageComponent";
4 | export * from "./ListBlockComponent";
5 | export * from "./StackComponent";
6 | export * from "./TableBlockComponent";
7 | export * from "./TextBlockComponent";
8 | export * from "./TimeStampComponent";
9 | export * from "./ColumnComponent";
10 |
--------------------------------------------------------------------------------
/src/livepdf/styles/index.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 |
3 | export const PDFViewerStyle = style({
4 | $debugName: "PDFViewerStyle",
5 | height: "100%",
6 | width: "100%"
7 | });
8 |
--------------------------------------------------------------------------------
/src/models/getString.ts:
--------------------------------------------------------------------------------
1 | import * as en from "./languages/en";
2 | import * as pl from "./languages/pl";
3 |
4 | export const Languages = {
5 | pl: (pl as unknown) as typeof en,
6 | en: (en as unknown) as typeof en
7 | };
8 |
9 | export const getString = (language: T) => <
10 | R extends keyof typeof Languages[T]
11 | >(
12 | key: R
13 | ) => (value: S) => {
14 | if (!Languages[language]) {
15 | throw new Error(`No such language ${language}`);
16 | }
17 | if (!Languages[language][key]) {
18 | throw new Error(
19 | `No such key ${key} in language ${language} - please implement it in models/languages/${language}.ts`
20 | );
21 | }
22 | if (!Languages[language][key][value]) {
23 | throw new Error(
24 | `No such value in language ${language} with key ${key} - please implement it in models/languages/${language}.ts`
25 | );
26 | }
27 | return Languages[language][key][value];
28 | };
29 |
--------------------------------------------------------------------------------
/src/models/index.ts:
--------------------------------------------------------------------------------
1 | import { getString } from "./getString";
2 |
3 | export const translated = getString("en");
4 |
--------------------------------------------------------------------------------
/src/models/languages/en.ts:
--------------------------------------------------------------------------------
1 | export enum ControlsComponentTxt {
2 | ButtonStack = "Stack",
3 | ButtonText = "Text",
4 | ButtonImage = "Image",
5 | ButtonList = "List",
6 | ButtonVerticalSplit = "Vertical split",
7 | ButtonTable = "Table",
8 | ButtonTimeStamp = "Time stamp"
9 | }
10 |
11 | export enum ColumnsCoponentTxt {
12 | IconTrashDelete = "Delete"
13 | }
14 |
15 | export enum DocumentComponentTxt {
16 | DeletePage = "Delete page"
17 | }
18 |
19 | export enum ImageComponentTxt {
20 | ImageTitle = "Image",
21 | PlaceholderWidth = "width",
22 | PlaceholderHeight = "height"
23 | }
24 |
25 | export enum ListBlockComponentTxt {
26 | TitleList = "List"
27 | }
28 |
29 | export enum StackComponentTxt {
30 | TitleStack = "Stack"
31 | }
32 |
33 | export enum TableBlockComponentTxt {
34 | TableTitle = "table",
35 | ControleButtonDelete = "Delete",
36 | ControleButtonAddBlock = "Add block",
37 | ControleButtonAddColumn = "Add column"
38 | }
39 |
40 | export enum TimeStampComponentTxt {
41 | Date = "Date"
42 | }
43 | export enum ConfirmTxt {
44 | AreYouSure = "Are you sure",
45 | Yes = "Yes",
46 | No = "No"
47 | }
48 |
49 | export enum ReactPDFEditorTxt {
50 | Loading = "Loading...",
51 | HidePreview = "Hide preview",
52 | ShowPreview = "Show preview",
53 | SectionTitleHeader = "Header",
54 | SectionTitlePages = "Pages",
55 | ButtonAddPage = "Add Page",
56 | SectionTitleFooter = "Footer"
57 | }
58 | export enum TextBlockComponentTxt {
59 | TextBlockTitle = "Text"
60 | }
--------------------------------------------------------------------------------
/src/models/languages/pl.ts:
--------------------------------------------------------------------------------
1 | export enum ControlsComponentTxt {
2 | ButtonStack = "Stack",
3 | ButtonText = "Text",
4 | ButtonImage = "Image",
5 | ButtonList = "List",
6 | ButtonVerticalSplit = "Vertical split",
7 | ButtonTable = "Table",
8 | ButtonTimeStamp = "Time stamp"
9 | }
10 |
11 | export enum ColumnsCoponentTxt {
12 | IconTrashDelete = "Usuń"
13 | }
14 |
15 | export enum DocumentComponentTxt {
16 | DeletePage = "Usuń stronę"
17 | }
18 |
19 | export enum ImageComponentTxt {
20 | ImageTitle = "Obraz",
21 | PlaceholderWidth = "width",
22 | PlaceholderHeight = "height"
23 | }
24 |
25 | export enum ListBlockComponentTxt {
26 | TitleList = "Lista"
27 | }
28 |
29 | export enum StackComponentTxt {
30 | TitleStack = "Stack"
31 | }
32 |
33 | export enum TableBlockComponentTxt {
34 | TableTitle = "Tabela",
35 | ControleButtonDelete = "Usuń",
36 | ControleButtonAddBlock = "Dodaj rząd",
37 | ControleButtonAddColumn = "Dodaj kolumnę"
38 | }
39 |
40 | export enum TimeStampComponentTxt {
41 | Date = "Data"
42 | }
43 | export enum ConfirmTxt {
44 | AreYouSure = "Jesteś pewien",
45 | Yes = "Tak",
46 | No = "Nie"
47 | }
48 |
49 | export enum ReactPDFEditorTxt {
50 | Loading = "Loading...",
51 | HidePreview = "Ukryj pogląd",
52 | ShowPreview = "Pokaż pogląd",
53 | SectionTitleHeader = "Nagłowek",
54 | SectionTitlePages = "Strona",
55 | ButtonAddPage = "Dodaj stronę",
56 | SectionTitleFooter = "Stopka"
57 | }
58 | export enum TextBlockComponentTxt {
59 | TextBlockTitle = "Tekst"
60 | }
61 |
--------------------------------------------------------------------------------
/src/screens/InitialPDFModels.tsx:
--------------------------------------------------------------------------------
1 |
2 | export const initialModel = {
3 | PDF: {
4 | name: "untitled",
5 | template: {
6 | footer: {
7 | __typename: "Stack",
8 | styleJson: JSON.stringify({
9 | marginTop: 0,
10 | marginRight: 37,
11 | marginLeft: 37,
12 | marginBottom: 30
13 | }),
14 | items: []
15 | },
16 | header: {
17 | __typename: "Stack",
18 | items: []
19 | },
20 | margin: [10, 80, 10, 40],
21 | documents: [
22 | {
23 | features: {
24 | items: []
25 | }
26 | }
27 | ]
28 | }
29 | },
30 | showPDF: true
31 | }
32 |
--------------------------------------------------------------------------------
/src/screens/ReactPDFEditor.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { PartialObjects } from "@/graphql-zeus";
3 | import { DocumentComponent, FeatureComponent } from "@components/editor";
4 | import { IconButton } from "@components/atoms";
5 | import * as styles from "./styles/ReactPDFEditor";
6 | import { LivePDF } from "@/livepdf";
7 | import { FrontendTypes } from "@/frontend-types";
8 | import { translated } from "@/models";
9 | import * as Icons from "react-feather";
10 | export interface PDFProps {
11 | onChange?: (onChange: FrontendTypes["MachineTemplate"]) => void;
12 | initialPDF?: FrontendTypes["MachineTemplate"];
13 | }
14 |
15 |
16 | export interface PDFState {
17 | PDF: FrontendTypes["MachineTemplate"];
18 | editedFeature?: PartialObjects["Feature"];
19 | errors?: string[];
20 | showPDF: boolean;
21 | }
22 |
23 | const t = translated("ReactPDFEditorTxt");
24 |
25 | export const ReactPDFEditor: React.FunctionComponent = ({
26 | initialPDF,
27 | onChange
28 | }) => {
29 |
30 |
31 | const [state, setState] = useState(
32 | {
33 | PDF: initialPDF || {
34 | name: "untitled",
35 | template: {
36 | footer: {
37 | __typename: "Stack",
38 | styleJson: JSON.stringify({
39 | marginTop: 0,
40 | marginRight: 37,
41 | marginLeft: 37,
42 | marginBottom: 30
43 | }),
44 | items: []
45 | },
46 | header: {
47 | __typename: "Stack",
48 | items: []
49 | },
50 | margin: [10, 80, 10, 40],
51 | documents: [
52 | {
53 | features: {
54 | items: []
55 | }
56 | }
57 | ]
58 | }
59 | },
60 | showPDF: true
61 | }
62 | );
63 |
64 | onChange = () => {
65 | const currentPDFObject: FrontendTypes["MachineTemplate"] = {
66 | ...state.PDF
67 | };
68 | setState(s => ({
69 | ...s,
70 | PDF: currentPDFObject
71 | }));
72 | };
73 |
74 | const updatePDF = (
75 | fn: (
76 | PDF: FrontendTypes["MachineTemplate"]
77 | ) => FrontendTypes["MachineTemplate"]
78 | ) => {
79 | setState(s => ({
80 | ...s,
81 | PDF: fn(state.PDF)
82 | }));
83 | };
84 | const updatePDFTemplate = (
85 | fn: (
86 | template: FrontendTypes["MachineDocument"]
87 | ) => FrontendTypes["MachineDocument"]
88 | ) => {
89 | updatePDF(PDF => ({
90 | ...PDF,
91 | template: fn(PDF.template || {})
92 | }));
93 | };
94 | const updatePDFTemplateDocuments = (
95 | fn: (documents: FrontendTypes["Document"][]) => FrontendTypes["Document"][]
96 | ) => {
97 | updatePDFTemplate(template => ({
98 | ...template,
99 | documents: fn(template.documents || [])
100 | }));
101 | };
102 | const addPage = () => {
103 | updatePDFTemplateDocuments(documents => [
104 | ...documents,
105 | {
106 | features: {
107 | items: []
108 | }
109 | }
110 | ]);
111 | };
112 | const mutateWholeObject = () => {
113 | const currentPDFObject: FrontendTypes["MachineTemplate"] = {
114 | ...state.PDF
115 | };
116 | setState(s => ({
117 | ...s,
118 | PDF: currentPDFObject
119 | }));
120 | // forceUpdate();
121 | };
122 | const { PDF } = state;
123 | if (!PDF) {
124 | return {t("Loading")}
;
125 | }
126 | const { template } = PDF;
127 | if (!template) {
128 | return {t("Loading")}
;
129 | }
130 | const {
131 | documents = [{ features: { items: [] } }],
132 | footer,
133 | header
134 | } = template;
135 | return (
136 | <>
137 |
138 | setState(s => ({ ...s, showPDF: !state.showPDF }))}
140 | >
141 | {state.showPDF ? (
142 | <>
143 | {t("HidePreview")}
144 |
145 | >
146 | ) : (
147 | <>
148 | {t("ShowPreview")}
149 |
150 | >
151 | )}
152 |
153 |
154 |
155 |
156 | mutateWholeObject()}
159 | onDelete={() => { }}
160 | onEdit={editedFeature => setState(s => ({ ...s, editedFeature }))}
161 | hideControls={true}
162 | />
163 | {t("SectionTitlePages")}
164 | {documents.map((d, i) => (
165 | setState(s => ({ ...s, editedFeature }))}
170 | onChange={() => mutateWholeObject()}
171 | onDelete={() => {
172 | template.documents! = template!.documents!.filter(
173 | (doc, ind) => doc !== d
174 | );
175 | mutateWholeObject();
176 | }}
177 | />
178 | ))}
179 | addPage()} />
180 |
181 | {t("SectionTitleFooter")}
182 | mutateWholeObject()}
185 | onDelete={() => { }}
186 | onEdit={editedFeature => setState(s => ({ ...s, editedFeature }))}
187 | hideControls={true}
188 | />
189 |
190 | {state.showPDF && (
191 |
192 |
193 |
194 | )}
195 |
196 | >
197 | );
198 | };
199 |
--------------------------------------------------------------------------------
/src/screens/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ReactPDFEditor";
2 | export * from "./InitialPDFModels"
--------------------------------------------------------------------------------
/src/screens/styles/ReactPDFEditor.tsx:
--------------------------------------------------------------------------------
1 | import { style, fontFace } from "typestyle";
2 | import { Colors } from "@/Colors";
3 | import { TOPHEIGHT } from "@/constants";
4 |
5 | fontFace({
6 | fontFamily: "Fira Sans",
7 | fontStyle: "normal",
8 | fontWeight: 400,
9 | src: `local('Fira Sans Regular'), local('FiraSans-Regular'), url(https://fonts.gstatic.com/s/firasans/v10/va9E4kDNxMZdWfMOD5Vvl4jLazX3dA.woff2) format('woff2');`
10 | }),
11 | fontFace({
12 | fontFamily: "Fira Sans",
13 | fontStyle: "bold",
14 | fontWeight: 700,
15 | src: `local('Fira Sans Bold'), local('FiraSans-Bold'), url(https://fonts.gstatic.com/s/firasans/v10/va9B4kDNxMZdWfMOD5VnLK3eRhf6Xl7Glw.woff2) format('woff2');`
16 | });
17 |
18 | fontFace({
19 | fontFamily: "Fira Sans",
20 | fontStyle: "light",
21 | fontWeight: 300,
22 | src: `local('Fira Sans Light'), local('FiraSans-Light'), url(https://fonts.gstatic.com/s/firasans/v10/va9B4kDNxMZdWfMOD5VnPKreRhf6Xl7Glw.woff2) format('woff2');`
23 | });
24 |
25 | fontFace({
26 | fontFamily: "Fira Sans",
27 | fontStyle: "medium",
28 | fontWeight: 500,
29 | src: `local('Fira Sans Medium'), local('FiraSans-Medium'), url(https://fonts.gstatic.com/s/firasans/v10/va9B4kDNxMZdWfMOD5VnZKveRhf6Xl7Glw.woff2) format('woff2');`
30 | });
31 |
32 | export const Actions = style({
33 | display: "flex"
34 | });
35 | export const SplitScreen = style({
36 | $debugName: "SplitScreen",
37 | display: "flex",
38 | flexFlow: "row nowrap",
39 | alignItems: "stretch",
40 | height: `calc( 100% - ${TOPHEIGHT}px )`
41 | });
42 |
43 | export const Left = style({
44 | $debugName: "Left",
45 | flex: 1,
46 | padding: `${TOPHEIGHT}px 30px`,
47 | background: `#f9f9f9`,
48 | overflowY: "auto",
49 | height: "100%",
50 | $nest: {
51 | "*": {
52 | maxWidth: "100%"
53 | }
54 | }
55 | });
56 |
57 | export const Right = style({
58 | $debugName: "Right",
59 | width: "50%",
60 | height: "100%",
61 | overflowY: "hidden",
62 | background: `#f9f9f9`,
63 | padding: `${TOPHEIGHT}px 30px`,
64 | $nest: {
65 | "*": {
66 | maxWidth: "100%"
67 | }
68 | }
69 | });
70 |
71 | export const SectionTitle = style({
72 | $debugName: "SectionTitle",
73 | fontFamily: "Fira Sans",
74 | fontSize: 16,
75 | paddingBottom: 5,
76 | borderBottom: `1px dashed ${Colors["Ancient Stone"]}`
77 | });
78 |
79 | export const EyePlacement = style({
80 | $debugName: "EyePlacement",
81 | position: "absolute",
82 | right: 30,
83 | top: 0,
84 | height: TOPHEIGHT,
85 | display: "flex",
86 | alignItems: "center"
87 | });
88 |
--------------------------------------------------------------------------------
/src/topmenu/components/ExpandableInput.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import ReactPDF from "@react-pdf/renderer";
3 | import * as Icons from "react-feather";
4 | import { SmallInput } from "./SmallInput";
5 | import { TopIcon } from "./TopIcon";
6 | export const ExpandableInput = ({
7 | v,
8 | values,
9 | applyStyle
10 | }: {
11 | v: {
12 | tooltip: string;
13 | icon: keyof typeof Icons;
14 | };
15 | values: Array<{
16 | name: keyof ReactPDF.Style;
17 | tooltip: string;
18 | icon: keyof typeof Icons;
19 | v: string;
20 | }>;
21 | applyStyle: (css: ReactPDF.Style) => void;
22 | }) => {
23 | const allSame = [...new Set(values.map(v => v.v))];
24 | const canBeShrinked = allSame.length === 1;
25 | const [expanded, setExpanded] = useState(false);
26 | if (!expanded && canBeShrinked) {
27 | return (
28 | <>
29 | {
33 | setExpanded(true);
34 | }}
35 | />
36 | {}} />
37 | {
40 | applyStyle(
41 | values.reduce((a, b) => {
42 | a[b.name] = e;
43 | return a;
44 | }, {})
45 | );
46 | }}
47 | />
48 | >
49 | );
50 | }
51 | return (
52 | <>
53 | {canBeShrinked && (
54 | {
58 | setExpanded(false);
59 | }}
60 | />
61 | )}
62 | {values.map(v => (
63 | <>
64 | {}} />
65 | {
68 | applyStyle({
69 | [v.name]: e
70 | });
71 | }}
72 | />
73 | >
74 | ))}
75 | >
76 | );
77 | };
--------------------------------------------------------------------------------
/src/topmenu/components/SelectInput.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import * as styles from "./styles/SelectInput";
3 | import * as sharedStyles from "./styles/Shared";
4 | import { fonts } from "@/livepdf/PDFDocument";
5 |
6 | export const SelectInput = ({
7 | value,
8 | onChange
9 | }: {
10 | value: string;
11 | onChange: (e: string) => void;
12 | }) => {
13 | const [v, setV] = useState("Fira Sans");
14 | useEffect(() => {
15 | setV(value);
16 | }, [value]);
17 |
18 | const availableFonts = fonts.map((f: typeof fonts[0]) => f.family);
19 |
20 | return (
21 |
22 |
38 |
39 | );
40 | };
--------------------------------------------------------------------------------
/src/topmenu/components/SmallInput.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import * as styles from "./styles/SmallInput";
3 | import * as sharedStyles from "./styles/Shared";
4 | export const SmallInput = ({
5 | value,
6 | onChange
7 | }: {
8 | value: string;
9 | onChange: (e: string) => void;
10 | }) => {
11 | const [v, setV] = useState("0");
12 | useEffect(() => {
13 | setV(value);
14 | }, [value]);
15 | return (
16 |
17 | {
23 | setV(e.target.value);
24 | }}
25 | onBlur={() => {
26 | onChange(v);
27 | }}
28 | />
29 |
30 | );
31 | };
32 |
--------------------------------------------------------------------------------
/src/topmenu/components/Tooltip.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/33dc039c17d9fd5f911fa2f874f4afa5825809b1/src/topmenu/components/Tooltip.tsx
--------------------------------------------------------------------------------
/src/topmenu/components/TopIcon.tsx:
--------------------------------------------------------------------------------
1 |
2 | import React from "react";
3 | import * as styles from "./styles/Topicon";
4 | import Tooltip from "rc-tooltip";
5 | import * as Icons from "react-feather";
6 | import cx from "classnames";
7 |
8 | export const TopIcon = ({
9 | icon,
10 | tooltip,
11 | onClick,
12 | active
13 | }: {
14 | tooltip: string;
15 | icon: keyof typeof Icons;
16 | active?: boolean;
17 | onClick: () => void;
18 | }) => {
19 | const Ico = Icons[icon];
20 | return (
21 |
22 |
23 |
24 |
25 |
26 | );
27 | };
--------------------------------------------------------------------------------
/src/topmenu/components/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./SmallInput";
2 | export * from './TopIcon';
3 | export * from './ExpandableInput';
4 | export * from './SelectInput'
--------------------------------------------------------------------------------
/src/topmenu/components/styles/SelectInput.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | export const Select = style({
3 | $debugName: "TopMenuSelectInput",
4 |
5 |
6 | // background-color: #f1f1f1;
7 | width: '100%',
8 | border: 'none solid',
9 | borderRadius: '1px'
10 | });
11 |
--------------------------------------------------------------------------------
/src/topmenu/components/styles/Shared.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | import { Colors } from "@/Colors";
3 | import { TOPHEIGHT } from "@/constants";
4 | export const Main = style({
5 | $debugName: "TopMenuMain",
6 | fontFamily: "Fira Sans",
7 | background: Colors.White,
8 | display: "flex",
9 | boxShadow: `${Colors["Dark Side"]}44 2px 5px 14px`,
10 | height: TOPHEIGHT,
11 | paddingLeft: 30
12 | });
13 | export const Placement = style({
14 | $debugName: "TopMenuPlacement",
15 | padding: 5,
16 | display: "flex",
17 | alignItems: "center"
18 | });
19 |
--------------------------------------------------------------------------------
/src/topmenu/components/styles/SmallInput.tsx:
--------------------------------------------------------------------------------
1 | import { style } from "typestyle";
2 | export const Input = style({
3 | $debugName: "TopMenuInput",
4 | width: 40
5 | });
6 |
--------------------------------------------------------------------------------
/src/topmenu/components/styles/Topicon.tsx:
--------------------------------------------------------------------------------
1 | import { style, classes } from "typestyle";
2 | import { Colors } from "@/Colors";
3 | import { TOPHEIGHT } from "@/constants";
4 | export const Main = style({
5 | $debugName: "TopMenuMain",
6 | fontFamily: "Fira Sans",
7 | background: Colors.White,
8 | display: "flex",
9 | boxShadow: `${Colors["Dark Side"]}44 2px 5px 14px`,
10 | height: TOPHEIGHT,
11 | paddingLeft: 30
12 | });
13 | export const Placement = style({
14 | $debugName: "TopMenuPlacement",
15 | padding: 5,
16 | display: "flex",
17 | alignItems: "center"
18 | });
19 |
20 | export const Button = classes(
21 | Placement,
22 | style({
23 | $debugName: "TopMenuButton",
24 | cursor: "pointer",
25 | color: Colors["Androgyn"],
26 | $nest: {
27 | "&:hover,&.active": {
28 | color: Colors["Rising Embers"]
29 | }
30 | }
31 | })
32 | );
33 | export const Input = style({
34 | $debugName: "TopMenuInput",
35 | width: 40
36 | });
37 | export const ColorPicker = style({
38 | $debugName: "TopMenuColorPicker",
39 | marginTop: 10
40 | });
41 |
--------------------------------------------------------------------------------
/src/topmenu/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as styles from "@components/styles/TopMenu";
3 | import "rc-tooltip/assets/bootstrap_white.css";
4 | import "rc-color-picker/assets/index.css";
5 | import ReactPDF from "@react-pdf/renderer";
6 | import { PartialObjects } from "@/graphql-zeus";
7 | import { DefaultValues } from "@/constants";
8 | import {
9 | SmallInput,
10 | TopIcon,
11 | ExpandableInput,
12 | SelectInput
13 | } from "./components";
14 | import {
15 | ExpandableInputMaximize2,
16 | ExpandableInputMinimize2,
17 | ExpandableInputSquare,
18 | TopIconAlignCenter,
19 | TopIconAlignLeft,
20 | TopIconAlignRight,
21 | TopIconBold,
22 | TopIconTextDecoration,
23 | TopIconItalic,
24 | InputFontSize,
25 | InputLineHeight,
26 | alignItemsToBaselineIcon,
27 | alignItemsToCenterIcon,
28 | alignItemsToEndIcon,
29 | alignItemsToStartIcon,
30 | alignItemsToStretchIcon,
31 | alignSelfToFlexBaselineIcon,
32 | alignSelfToFlexCenterIcon,
33 | alignSelfToFlexEndIcon,
34 | alignSelfToFlexStartIcon,
35 | alignSelfToFlexStretchIcon,
36 | fitToParentIcon,
37 | flexFlowColumnIcon,
38 | flexFlowRowIcon,
39 | justifyContentCenterIcon,
40 | justifyContentFlexEndIcon,
41 | justifyContentFlexStartIcon,
42 | justifyContentSpaceAround,
43 | justifyContentSpaceBetween,
44 | TopIconAlignJustify,
45 | TopIconFontSize,
46 | SelectFontType,
47 | TopIconRedo,
48 | TopIconUndo
49 | } from "./items";
50 | import {
51 | MenuItem,
52 | MenuItemType,
53 | ColorPickerMenuItem,
54 | ConditionalGroupMenuItem
55 | } from "./models";
56 |
57 | const ColorPicker = require("rc-color-picker");
58 |
59 | const defaultColorPicker: ColorPickerMenuItem = {
60 | itemType: MenuItemType.ColorPicker
61 | };
62 |
63 | const flexDirectionRowJustifyConditionalGroup: ConditionalGroupMenuItem = {
64 | itemType: MenuItemType.ConditionalGroup,
65 | active: (style: ReactPDF.Style): boolean => style.flexDirection === "row",
66 | children: [
67 | justifyContentFlexStartIcon,
68 | justifyContentFlexEndIcon,
69 | justifyContentCenterIcon,
70 | justifyContentSpaceBetween,
71 | justifyContentSpaceAround,
72 |
73 | alignSelfToFlexStartIcon,
74 | alignSelfToFlexEndIcon,
75 | alignSelfToFlexCenterIcon,
76 | alignSelfToFlexBaselineIcon,
77 | alignSelfToFlexStretchIcon,
78 |
79 | alignItemsToStartIcon,
80 | alignItemsToEndIcon,
81 | alignItemsToCenterIcon,
82 | alignItemsToBaselineIcon,
83 | alignItemsToStretchIcon
84 | ]
85 | };
86 |
87 | const flexDirectionColumnJustifyConditionalGroup: ConditionalGroupMenuItem = {
88 | itemType: MenuItemType.ConditionalGroup,
89 | active: (style: ReactPDF.Style): boolean => style.flexDirection === "column",
90 | children: [
91 | justifyContentFlexStartIcon,
92 | justifyContentFlexEndIcon,
93 | justifyContentCenterIcon,
94 | justifyContentSpaceBetween,
95 | justifyContentSpaceAround,
96 |
97 | alignSelfToFlexStartIcon,
98 | alignSelfToFlexEndIcon,
99 | alignSelfToFlexCenterIcon,
100 | alignSelfToFlexBaselineIcon,
101 | alignSelfToFlexStretchIcon,
102 |
103 | alignItemsToStartIcon,
104 | alignItemsToEndIcon,
105 | alignItemsToCenterIcon,
106 | alignItemsToBaselineIcon,
107 | alignItemsToStretchIcon
108 | ]
109 | };
110 |
111 | const common: MenuItem[] = [
112 |
113 | fitToParentIcon,
114 | TopIconRedo,
115 | TopIconUndo,
116 | ExpandableInputMaximize2,
117 | ExpandableInputMinimize2,
118 | ColorPicker,
119 | TopIconBold,
120 | TopIconItalic,
121 | InputLineHeight,
122 | TopIconTextDecoration,
123 | TopIconFontSize,
124 | InputFontSize,
125 | TopIconAlignLeft,
126 | TopIconAlignCenter,
127 | TopIconAlignRight,
128 | TopIconAlignJustify,
129 | ColorPicker,
130 | ExpandableInputSquare,
131 | SelectFontType
132 | ];
133 |
134 | const configurations: {
135 | [k in
136 | | "Start"
137 | | "TextBlock"
138 | | "Image"
139 | | "TableBlock"
140 | | "Columns"
141 | | "Stack"
142 | | "ListBlock"
143 | | "TimeStamp"]: ConditionalGroupMenuItem;
144 | } = {
145 | Start: {
146 | itemType: MenuItemType.ConditionalGroup,
147 | active: (style: ReactPDF.Style) => true,
148 | children: [
149 | fitToParentIcon,
150 | ExpandableInputMaximize2,
151 | ExpandableInputMinimize2,
152 | ColorPicker,
153 | TopIconBold,
154 | TopIconFontSize,
155 | InputFontSize,
156 | TopIconAlignLeft,
157 | TopIconAlignCenter,
158 | TopIconAlignRight,
159 | TopIconAlignJustify,
160 | ColorPicker,
161 | ExpandableInputSquare,
162 | SelectFontType
163 | ]
164 | },
165 |
166 | TextBlock: {
167 | itemType: MenuItemType.ConditionalGroup,
168 | active: (style: ReactPDF.Style) => true,
169 | children: common
170 | },
171 | Image: {
172 | itemType: MenuItemType.ConditionalGroup,
173 | active: (style: ReactPDF.Style) => true,
174 | children: common
175 | },
176 | TableBlock: {
177 | itemType: MenuItemType.ConditionalGroup,
178 | active: (style: ReactPDF.Style) => true,
179 | children: common
180 | },
181 | Columns: {
182 | itemType: MenuItemType.ConditionalGroup,
183 | active: (style: ReactPDF.Style) => true,
184 | children: [fitToParentIcon]
185 | },
186 | Stack: {
187 | itemType: MenuItemType.ConditionalGroup,
188 | active: (style: ReactPDF.Style) => true,
189 | children: [
190 | fitToParentIcon,
191 | TopIconUndo,
192 | TopIconRedo,
193 | flexFlowRowIcon,
194 | flexFlowColumnIcon,
195 | flexDirectionRowJustifyConditionalGroup,
196 | flexDirectionColumnJustifyConditionalGroup,
197 | alignSelfToFlexStartIcon,
198 | alignSelfToFlexEndIcon,
199 | alignSelfToFlexCenterIcon,
200 | alignSelfToFlexStretchIcon,
201 | alignSelfToFlexBaselineIcon,
202 | ExpandableInputMaximize2,
203 | ExpandableInputMinimize2,
204 | ColorPicker,
205 | TopIconAlignLeft,
206 | TopIconAlignCenter,
207 | TopIconAlignRight,
208 | TopIconAlignJustify,
209 | defaultColorPicker,
210 | ExpandableInputSquare
211 | ]
212 | },
213 | ListBlock: {
214 | itemType: MenuItemType.ConditionalGroup,
215 | active: (style: ReactPDF.Style) => true,
216 | children: common
217 | },
218 | TimeStamp: {
219 | itemType: MenuItemType.ConditionalGroup,
220 | active: (style: ReactPDF.Style) => true,
221 | children: common
222 | }
223 | };
224 |
225 | const ConditionalGroup: React.FunctionComponent<{
226 | active: boolean;
227 | style: ReactPDF.Style;
228 | applyStyle: (style: ReactPDF.Style) => void;
229 | items: MenuItem[];
230 | }> = ({ active, style, applyStyle, items }) => {
231 | if (!active) {
232 | return null;
233 | }
234 | const valueOrDefault = (name: keyof ReactPDF.Style) =>
235 | `${style[name] || DefaultValues[name]}`;
236 | return (
237 | <>
238 | {items.map(item => {
239 | if (item.itemType === MenuItemType.TopIcon) {
240 | return (
241 | {
245 | applyStyle(item.change(style));
246 | }}
247 | active={item.active(style)}
248 | />
249 | );
250 | }
251 | if (item.itemType === MenuItemType.SmallInput) {
252 | return (
253 | applyStyle(item.change(e))}
256 | />
257 | );
258 | }
259 | if (item.itemType === MenuItemType.SelectInput) {
260 | return (
261 | applyStyle(item.change(e))}
264 | />
265 | );
266 | }
267 |
268 | if (item.itemType === MenuItemType.ExtendableInput) {
269 | return (
270 | ({
274 | ...v,
275 | v: valueOrDefault(v.name)
276 | }))}
277 | />
278 | );
279 | }
280 | if (item.itemType === MenuItemType.ColorPicker) {
281 | return (
282 | {
286 | applyStyle({ color });
287 | }}
288 | />
289 | );
290 | }
291 | if (item.itemType === MenuItemType.ConditionalGroup) {
292 | return (
293 |
299 | );
300 | }
301 | return null;
302 | })}
303 | >
304 | );
305 | };
306 |
307 | export interface TopMenuProps {
308 | editedFeature?: PartialObjects["Feature"];
309 | onChange: () => void;
310 | editTopMenu?: boolean;
311 | style?: React.CSSProperties;
312 | }
313 | export const TopMenu = ({ editedFeature = {}, onChange }: TopMenuProps) => {
314 | const editedFeatureStyle: ReactPDF.Style = editedFeature.styleJson
315 | ? JSON.parse(editedFeature.styleJson)
316 | : {};
317 | const applyStyle = (css: ReactPDF.Style) => {
318 | editedFeature.styleJson = JSON.stringify({
319 | ...editedFeatureStyle,
320 | ...css
321 | });
322 | onChange();
323 | };
324 |
325 | const typename = editedFeature.__typename;
326 | const featureConfig =
327 | (typename && configurations[typename]) || configurations.Start;
328 | return (
329 |
330 |
336 |
337 | );
338 | };
339 |
--------------------------------------------------------------------------------
/src/topmenu/items/ExpandableInputMaximize2.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, ExtendableInputMenuItem } from "@/topmenu/models";
2 |
3 | export const ExpandableInputMaximize2: ExtendableInputMenuItem = {
4 | itemType: MenuItemType.ExtendableInput,
5 | v: { icon: "Maximize2", tooltip: "Margin" },
6 | values: [
7 | {
8 | icon: "ArrowUp",
9 | tooltip: "Margin top",
10 | name: "marginTop"
11 | },
12 | {
13 | icon: "ArrowRight",
14 | tooltip: "Margin right",
15 | name: "marginRight"
16 | },
17 | {
18 | icon: "ArrowDown",
19 | tooltip: "Margin bottom",
20 | name: "marginBottom"
21 | },
22 | {
23 | icon: "ArrowLeft",
24 | tooltip: "Margin Left",
25 | name: "marginLeft"
26 | }
27 | ]
28 | };
29 |
--------------------------------------------------------------------------------
/src/topmenu/items/ExpandableInputMinimize2.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, ExtendableInputMenuItem } from "@/topmenu/models";
2 |
3 | export const ExpandableInputMinimize2: ExtendableInputMenuItem = {
4 | itemType: MenuItemType.ExtendableInput,
5 | v: { icon: "Minimize2", tooltip: "Padding" },
6 | values: [
7 | {
8 | icon: "ArrowUp",
9 | tooltip: "Padding up",
10 | name: "paddingTop"
11 | },
12 | {
13 | icon: "ArrowRight",
14 | tooltip: "Padding right",
15 | name: "paddingRight"
16 | },
17 | {
18 | icon: "ArrowDown",
19 | tooltip: "Padding bottom",
20 | name: "paddingBottom"
21 | },
22 | {
23 | icon: "ArrowLeft",
24 | tooltip: "Padding Left",
25 | name: "paddingBottom"
26 | }
27 | ]
28 | };
29 |
--------------------------------------------------------------------------------
/src/topmenu/items/ExpandableInputSquare.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, ExtendableInputMenuItem } from "@/topmenu/models";
2 |
3 | export const ExpandableInputSquare: ExtendableInputMenuItem = {
4 | itemType: MenuItemType.ExtendableInput,
5 | v: { icon: "Square", tooltip: "Border width" },
6 | values: [
7 | {
8 | icon: "ArrowUp",
9 | tooltip: "Border Top Width",
10 | name: "borderTopWidth"
11 | },
12 | {
13 | icon: "ArrowRight",
14 | tooltip: "Border Right Width",
15 | name: "borderRightWidth"
16 | },
17 | {
18 | icon: "ArrowDown",
19 | tooltip: "Border Down Width",
20 | name: "borderBottomWidth"
21 | },
22 | {
23 | icon: "ArrowLeft",
24 | tooltip: "Bottom Left Width",
25 | name: "borderLeftWidth"
26 | }
27 | ]
28 | };
29 |
--------------------------------------------------------------------------------
/src/topmenu/items/InputFontSize.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, SmallInputMenuItem } from "@/topmenu/models";
2 |
3 | export const InputFontSize: SmallInputMenuItem = {
4 | name: "fontSize",
5 | itemType: MenuItemType.SmallInput,
6 | change: (e: string) => ({ fontSize: e })
7 | };
--------------------------------------------------------------------------------
/src/topmenu/items/InputLineHeight.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, SmallInputMenuItem } from "@/topmenu/models";
2 |
3 | export const InputLineHeight: SmallInputMenuItem = {
4 | name: "lineHeight",
5 | itemType: MenuItemType.SmallInput,
6 | change: (e: string) => ({ lineHeight: e })
7 | };
8 |
--------------------------------------------------------------------------------
/src/topmenu/items/SelectFontType.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, SelectInputItem } from "@/topmenu/models";
2 |
3 |
4 | export const SelectFontType: SelectInputItem = {
5 | name: "fontFamily",
6 | itemType: MenuItemType.SelectInput,
7 | change: (e: string) => ({ fontFamily: e })
8 | };
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconAlignCenter.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconAlignCenter: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Text align center",
7 | icon: "AlignCenter",
8 | change: (style: ReactPDF.Style) => ({
9 | textAlign: style.textAlign === "center" ? undefined : "center"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.textAlign === "center"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconAlignJustify.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconAlignJustify: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Text align justify",
7 | icon: "AlignJustify",
8 | change: (style: ReactPDF.Style) => ({
9 | textAlign: style.textAlign === "justify" ? undefined : "justify"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.textAlign === "justify"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconAlignLeft.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconAlignLeft: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Text align left",
7 | icon: "AlignLeft",
8 | change: (style: ReactPDF.Style) => ({
9 | textAlign: style.textAlign === "left" ? undefined : "left"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.textAlign === "left"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconAlignRight.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconAlignRight: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Text align right",
7 | icon: "AlignRight",
8 | change: (style: ReactPDF.Style) => ({
9 | textAlign: style.textAlign === "right" ? undefined : "right"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.textAlign === "right"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconBold.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconBold: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Bold",
7 | icon: "Bold",
8 | change: (style: ReactPDF.Style) => ({
9 | fontWeight: style.fontWeight === "bold" ? undefined : "bold"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.fontWeight === "bold"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconFontSize.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconFontSize: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Font Size",
7 | icon: "Type",
8 | change: (style: ReactPDF.Style) => ({
9 | fontWeight: style.fontWeight === "bold" ? undefined : "bold"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.fontWeight === "bold"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconItalic.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconItalic: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Italic",
7 | icon: "Italic",
8 | change: (style: ReactPDF.Style) => ({
9 | fontStyle: style.fontStyle === "italic" ? undefined : "italic"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.fontStyle === "italic"
12 | };
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconRedo.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconRedo: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Redo",
7 | icon: "ArrowRight",
8 | change: (style: ReactPDF.Style) => ({
9 | fontWeight: style.fontWeight === "bold" ? undefined : "bold"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.fontWeight === "bold"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconTextDecoration.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconTextDecoration: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Text decoration",
7 | icon: "Bold",
8 | change: (style: ReactPDF.Style) => ({
9 | textDecoration: style.textDecoration === "underline" ? undefined : "underline"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.textDecoration === "underline"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/TopIconUndo.tsx:
--------------------------------------------------------------------------------
1 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
2 | import ReactPDF from "@react-pdf/renderer";
3 |
4 | export const TopIconUndo: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "Undo",
7 | icon: "ArrowLeft",
8 | change: (style: ReactPDF.Style) => ({
9 | fontWeight: style.fontWeight === "bold" ? undefined : "bold"
10 | }),
11 | active: (style: ReactPDF.Style): boolean => style.fontWeight === "bold"
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignItemsToBaselineIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignItemsToBaselineIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "align items baseline",
7 | icon: "ArrowDownCircle",
8 | active: (style: ReactPDF.Style): boolean => style.alignItems === "baseline",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignItems: style.alignItems === "baseline" ? undefined : "baseline"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignItemsToCenterIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignItemsToCenterIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "align items to center",
7 | icon: "Circle",
8 | active: (style: ReactPDF.Style): boolean => style.alignItems === "center",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignItems: style.alignItems === "center" ? undefined : "center"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignItemsToEndIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignItemsToEndIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "align items to end",
7 | icon: "ArrowDownCircle",
8 | active: (style: ReactPDF.Style): boolean => style.alignItems === "flex-end",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignItems: style.alignItems === "flex-end" ? undefined : "flex-end"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignItemsToStartIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignItemsToStartIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "align items to start",
7 | icon: "ArrowUpCircle",
8 | active: (style: ReactPDF.Style): boolean => style.alignItems === "flex-start",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignItems: style.alignItems === "flex-start" ? undefined : "flex-start"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignItemsToStretchIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignItemsToStretchIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "align items stretch",
7 | icon: "ArrowDownCircle",
8 | active: (style: ReactPDF.Style): boolean => style.alignItems === "stretch",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignItems: style.alignItems === "stretch" ? undefined : "stretch"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignSelfToFlexBaselineIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignSelfToFlexBaselineIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | icon: "Circle",
7 | tooltip: "align self to baseline",
8 | active: (style: ReactPDF.Style): boolean => style.alignSelf === "baseline",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignSelf: style.alignSelf === "baseline" ? undefined : "baseline"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignSelfToFlexCenterIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignSelfToFlexCenterIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | icon: "Circle",
7 | tooltip: "align self to center",
8 | active: (style: ReactPDF.Style): boolean => style.alignSelf === "center",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignSelf: style.alignSelf === "center" ? undefined : "center"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignSelfToFlexEndIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignSelfToFlexEndIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | icon: "ArrowDown",
7 | tooltip: "align self to flex-end",
8 | active: (style: ReactPDF.Style): boolean => style.alignSelf === "flex-end",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignSelf: style.alignSelf === "flex-end" ? undefined : "flex-end"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignSelfToFlexStartIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignSelfToFlexStartIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | icon: "ArrowUp",
7 | tooltip: "align self to flex-start",
8 | active: (style: ReactPDF.Style): boolean => style.alignSelf === "flex-start",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignSelf: style.alignSelf === "flex-start" ? undefined : "flex-start"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/alignSelfToFlexStretchIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const alignSelfToFlexStretchIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | icon: "Circle",
7 | tooltip: "align self to stretch",
8 | active: (style: ReactPDF.Style): boolean => style.alignSelf === "stretch",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | alignSelf: style.alignSelf === "stretch" ? undefined : "stretch"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/fitToParentIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const fitToParentIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | icon: "Move",
7 | tooltip: "Fit to parent",
8 | active: (style: ReactPDF.Style): boolean => style.width === "100%",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | width: style.width === "100%" ? undefined : "100%"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/flexFlowColumnIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const flexFlowColumnIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "flex flow column",
7 | icon: "MoreVertical",
8 | active: (style: ReactPDF.Style): boolean => style.flexDirection === "column",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | flexDirection: style.flexDirection === "column" ? undefined : "column"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/flexFlowRowIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const flexFlowRowIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "flex flow row",
7 | icon: "MoreHorizontal",
8 | active: (style: ReactPDF.Style): boolean => style.flexDirection === "row",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | flexDirection: style.flexDirection === "row" ? undefined : "row"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./fitToParentIcon";
2 | // alginItems
3 | export * from "./alignItemsToBaselineIcon";
4 | export * from "./alignItemsToCenterIcon";
5 | export * from "./alignItemsToEndIcon";
6 | export * from "./alignItemsToStartIcon";
7 | export * from "./alignItemsToStretchIcon";
8 | // alginSelf
9 | export * from "./alignSelfToFlexBaselineIcon";
10 | export * from "./alignSelfToFlexCenterIcon";
11 | export * from "./alignSelfToFlexEndIcon";
12 | export * from "./alignSelfToFlexStartIcon";
13 | export * from "./alignSelfToFlexStretchIcon";
14 | // expamdablbeInput
15 | export * from "./ExpandableInputMaximize2";
16 | export * from "./ExpandableInputMinimize2";
17 | export * from "./ExpandableInputSquare";
18 | // fitToParent
19 | export * from "./fitToParentIcon";
20 | // flexFlow
21 | export * from "./flexFlowColumnIcon";
22 | export * from "./flexFlowRowIcon";
23 | // justifyContent
24 | export * from "./justifyContentCenterIcon";
25 | export * from "./justifyContentFlexEndIcon";
26 | export * from "./justifyContentFlexStartIcon";
27 | export * from "./justifyContentSpaceAround";
28 | export * from "./justifyContentSpaceBetween";
29 | // TopIcon
30 | export * from "./TopIconAlignCenter";
31 | export * from "./TopIconAlignJustify";
32 | export * from "./TopIconAlignLeft";
33 | export * from "./TopIconAlignRight";
34 | export * from "./TopIconBold";
35 | export * from "./TopIconItalic";
36 | export * from "./TopIconTextDecoration";
37 | export * from "./InputFontSize";
38 | export * from "./InputLineHeight";
39 | export * from "./TopIconFontSize";
40 | export * from "./SelectFontType";
41 | // Undo/Redo
42 | export * from "./TopIconUndo";
43 | export * from "./TopIconRedo";
44 |
--------------------------------------------------------------------------------
/src/topmenu/items/justifyContentCenterIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const justifyContentCenterIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "justify content center",
7 | icon: "Circle",
8 | active: (style: ReactPDF.Style): boolean => style.justifyContent === "center",
9 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
10 | justifyContent: style.justifyContent === "center" ? undefined : "center"
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/topmenu/items/justifyContentFlexEndIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const justifyContentFlexEndIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "justify content flex-end",
7 | icon: "ArrowRightCircle",
8 | active: (style: ReactPDF.Style): boolean =>
9 | style.justifyContent === "flex-end",
10 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
11 | justifyContent: style.justifyContent === "flex-end" ? undefined : "flex-end"
12 | })
13 | };
14 |
--------------------------------------------------------------------------------
/src/topmenu/items/justifyContentFlexStartIcon.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const justifyContentFlexStartIcon: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "justify content flex-start",
7 | icon: "ArrowLeftCircle",
8 | active: (style: ReactPDF.Style): boolean =>
9 | style.justifyContent === "flex-start",
10 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
11 | justifyContent:
12 | style.justifyContent === "flex-start" ? undefined : "flex-start"
13 | })
14 | };
15 |
--------------------------------------------------------------------------------
/src/topmenu/items/justifyContentSpaceAround.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const justifyContentSpaceAround: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "justify content space-around",
7 | icon: "ArrowRightCircle",
8 | active: (style: ReactPDF.Style): boolean =>
9 | style.justifyContent === "space-around",
10 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
11 | justifyContent:
12 | style.justifyContent === "space-around" ? undefined : "space-around"
13 | })
14 | };
15 |
--------------------------------------------------------------------------------
/src/topmenu/items/justifyContentSpaceBetween.tsx:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import { MenuItemType, IconMenuItem } from "@/topmenu/models";
3 |
4 | export const justifyContentSpaceBetween: IconMenuItem = {
5 | itemType: MenuItemType.TopIcon,
6 | tooltip: "justify content space-between",
7 | icon: "ArrowRightCircle",
8 | active: (style: ReactPDF.Style): boolean =>
9 | style.justifyContent === "space-between",
10 | change: (style: ReactPDF.Style): ReactPDF.Style => ({
11 | justifyContent:
12 | style.justifyContent === "space-between" ? undefined : "space-between"
13 | })
14 | };
15 |
--------------------------------------------------------------------------------
/src/topmenu/models/index.ts:
--------------------------------------------------------------------------------
1 | import ReactPDF from "@react-pdf/renderer";
2 | import * as Icons from "react-feather";
3 |
4 | export enum MenuItemType {
5 | TopIcon = "TopIcon",
6 | SmallInput = "SmallInput",
7 | ExtendableInput = "ExtendableInput",
8 | ColorPicker = "ColorPicker",
9 | ConditionalGroup = "Group",
10 | SelectInput="SelectInput"
11 | }
12 |
13 |
14 | //typ interface IconMenu ktory wskazuje z czego powinno sie sklładać IconMenuItem
15 | // itemType - pobiera typ ikon określonych w menuitemType -
16 | // icon - iformuje iz musi sciagnać z (tego musze sie nauczyć keyof typeof) z ikon /react-father
17 | // tooltip informajca o prosty string kory jest w propsach
18 | // active - fukcja ktora przypisuje styl elementu przez active
19 |
20 | export interface IconMenuItem {
21 | itemType: MenuItemType.TopIcon;
22 | icon: keyof typeof Icons;
23 | tooltip: string;
24 | active: (style: ReactPDF.Style) => boolean;
25 | change: (style: ReactPDF.Style) => ReactPDF.Style;
26 | }
27 |
28 | //typ topIcon
29 |
30 | export interface SmallInputMenuItem {
31 | itemType: MenuItemType.SmallInput;
32 | name: keyof ReactPDF.Style;
33 | change: (e: string) => ReactPDF.Style;
34 | }
35 |
36 | export interface SelectInputItem {
37 | itemType: MenuItemType.SelectInput
38 | name: keyof ReactPDF.Style
39 | change: (e:string) =>ReactPDF.Style
40 | }
41 |
42 | export interface ExtendableInputMenuItem {
43 | itemType: MenuItemType.ExtendableInput;
44 | v: {
45 | tooltip: string;
46 | icon: keyof typeof Icons;
47 | };
48 | values: Array<{
49 | name: keyof ReactPDF.Style;
50 | tooltip: string;
51 | icon: keyof typeof Icons;
52 | }>;
53 | }
54 |
55 | export interface ColorPickerMenuItem {
56 | itemType: MenuItemType.ColorPicker;
57 | }
58 |
59 | export interface ConditionalGroupMenuItem {
60 | itemType: MenuItemType.ConditionalGroup;
61 | active: (style: ReactPDF.Style) => boolean;
62 | children: MenuItem[];
63 | }
64 |
65 | export type MenuItem =
66 | | IconMenuItem
67 | | SmallInputMenuItem
68 | | ExtendableInputMenuItem
69 | | ColorPickerMenuItem
70 | | ConditionalGroupMenuItem
71 | | SelectInputItem
72 | ;
73 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { PartialObjects } from "./graphql-zeus";
2 | import { FrontendTypes } from "./frontend-types";
3 |
4 | export const isTextBlock = (arg: any): arg is PartialObjects["TextBlock"] => {
5 | return arg.__typename === "TextBlock";
6 | };
7 | export const isColumns = (arg: any): arg is PartialObjects["Columns"] => {
8 | return arg.__typename === "Columns";
9 | };
10 | export const isColumn = (arg: any): arg is PartialObjects["Column"] => {
11 | return arg.__typename === "Column";
12 | };
13 | export const isStack = (arg: any): arg is PartialObjects["Stack"] => {
14 | return arg.__typename === "Stack";
15 | };
16 | export const isListBlock = (arg: any): arg is PartialObjects["ListBlock"] => {
17 | return arg.__typename === "ListBlock";
18 | };
19 | export const isTableBlock = (arg: any): arg is PartialObjects["TableBlock"] => {
20 | return arg.__typename === "TableBlock";
21 | };
22 | export const isImage = (arg: any): arg is PartialObjects["Image"] => {
23 | return arg.__typename === "Image";
24 | };
25 | export const isFeature = (arg: any): arg is PartialObjects["Feature"] => {
26 | return !!arg.__typename;
27 | };
28 | export const isTimeStamp = (arg: any): arg is PartialObjects["TimeStamp"] => {
29 | return arg.__typename === "TimeStamp";
30 | };
31 |
32 | export const downloadPDF = (name: string, pdf: string) => {
33 | const linkSource = pdf;
34 | const downloadLink = document.createElement("a");
35 | const fileName = `${name}.pdf`;
36 |
37 | downloadLink.href = linkSource;
38 | downloadLink.download = fileName;
39 | downloadLink.click();
40 | };
41 |
42 | export const ConvertFeatureToFrontendFeature = (f: any) =>
43 | f as PartialObjects["Feature"];
44 | export const ConvertMachineDocumentToFrontendMachineDocument = (
45 | md: PartialObjects["MachineDocument"]
46 | ) => md as FrontendTypes["MachineDocument"];
47 |
48 | export const swapInArray = function(
49 | array: any[],
50 | index1: number,
51 | index2: number
52 | ) {
53 | if (index1 <= array.length && index2 <= array.length) {
54 | var temp = array[index2];
55 | array[index2] = array[index1];
56 | array[index1] = temp;
57 | }
58 | };
59 |
--------------------------------------------------------------------------------
/src/zeusSelectionSets.ts:
--------------------------------------------------------------------------------
1 | import { ZeusSelect, Style } from "./graphql-zeus";
2 |
3 | export const StyleSelectionSet = ZeusSelect