├── src ├── topmenu │ ├── components │ │ ├── Tooltip.tsx │ │ ├── index.tsx │ │ ├── styles │ │ │ ├── SmallInput.tsx │ │ │ ├── SelectInput.tsx │ │ │ ├── Shared.tsx │ │ │ └── Topicon.tsx │ │ ├── TopIcon.tsx │ │ ├── SmallInput.tsx │ │ ├── SelectInput.tsx │ │ └── ExpandableInput.tsx │ ├── items │ │ ├── InputFontSize.tsx │ │ ├── SelectFontType.tsx │ │ ├── InputLineHeight.tsx │ │ ├── TopIconBold.tsx │ │ ├── TopIconRedo.tsx │ │ ├── TopIconUndo.tsx │ │ ├── TopIconFontSize.tsx │ │ ├── fitToParentIcon.tsx │ │ ├── TopIconAlignLeft.tsx │ │ ├── TopIconAlignRight.tsx │ │ ├── TopIconItalic.tsx │ │ ├── TopIconAlignCenter.tsx │ │ ├── TopIconAlignJustify.tsx │ │ ├── flexFlowRowIcon.tsx │ │ ├── TopIconTextDecoration.tsx │ │ ├── alignItemsToCenterIcon.tsx │ │ ├── alignSelfToFlexCenterIcon.tsx │ │ ├── flexFlowColumnIcon.tsx │ │ ├── alignItemsToEndIcon.tsx │ │ ├── alignSelfToFlexEndIcon.tsx │ │ ├── alignSelfToFlexStretchIcon.tsx │ │ ├── alignItemsToStretchIcon.tsx │ │ ├── alignItemsToBaselineIcon.tsx │ │ ├── alignItemsToStartIcon.tsx │ │ ├── alignSelfToFlexBaselineIcon.tsx │ │ ├── alignSelfToFlexStartIcon.tsx │ │ ├── justifyContentCenterIcon.tsx │ │ ├── justifyContentFlexEndIcon.tsx │ │ ├── justifyContentFlexStartIcon.tsx │ │ ├── justifyContentSpaceAround.tsx │ │ ├── justifyContentSpaceBetween.tsx │ │ ├── ExpandableInputMaximize2.tsx │ │ ├── ExpandableInputMinimize2.tsx │ │ ├── ExpandableInputSquare.tsx │ │ └── index.tsx │ ├── models │ │ └── index.ts │ └── index.tsx ├── index.tsx ├── screens │ ├── index.tsx │ ├── InitialPDFModels.tsx │ ├── styles │ │ └── ReactPDFEditor.tsx │ └── ReactPDFEditor.tsx ├── models │ ├── index.ts │ ├── getString.ts │ └── languages │ │ ├── en.ts │ │ └── pl.ts ├── components │ ├── editor │ │ ├── styles │ │ │ ├── Column.tsx │ │ │ ├── ListBlock.tsx │ │ │ ├── Stack.tsx │ │ │ ├── Columns.tsx │ │ │ ├── Image.tsx │ │ │ ├── TableBlock.tsx │ │ │ ├── TextBlock.tsx │ │ │ ├── Feature.tsx │ │ │ └── Controls.tsx │ │ ├── display │ │ │ ├── styles │ │ │ │ ├── SectionTitle.tsx │ │ │ │ └── Rolloutable.tsx │ │ │ ├── SectionTitle.tsx │ │ │ ├── TopMenuEdit.tsx │ │ │ ├── DeleteAndEdit.tsx │ │ │ └── Rolloutable.tsx │ │ ├── TimeStampComponent.tsx │ │ ├── index.tsx │ │ ├── EmptyFeatureComponent.tsx │ │ ├── ColumnComponent.tsx │ │ ├── DocumentComponent.tsx │ │ ├── TextBlockComponent.tsx │ │ ├── StackComponent.tsx │ │ ├── ListBlockComponent.tsx │ │ ├── ColumnsComponent.tsx │ │ ├── FeatureComponent.tsx │ │ ├── ImageComponent.tsx │ │ ├── Controls.tsx │ │ └── TableBlockComponent.tsx │ ├── index.tsx │ ├── atoms │ │ ├── index.tsx │ │ ├── IconButton.tsx │ │ ├── ButtonSimple.tsx │ │ └── Checkbox.tsx │ ├── styles │ │ ├── ButtonSimple.tsx │ │ ├── IconButton.tsx │ │ ├── TopMenu.tsx │ │ └── CheckboxStyles.tsx │ ├── molecules │ │ └── Confirm.tsx │ └── icons │ │ └── index.tsx ├── livepdf │ ├── fonts │ │ ├── FiraSans-Bold.ttf │ │ ├── FiraSans-Light.ttf │ │ ├── FiraSans-Medium.ttf │ │ └── FiraSans-Regular.ttf │ ├── styles │ │ └── index.tsx │ ├── livecomponents │ │ └── editor │ │ │ ├── index.tsx │ │ │ ├── TimeStampComponent.tsx │ │ │ ├── TextBlockComponent.tsx │ │ │ ├── StackComponent.tsx │ │ │ ├── ImageComponent.tsx │ │ │ ├── TableBlockComponent.tsx │ │ │ ├── ListBlockComponent.tsx │ │ │ ├── ColumnComponent.tsx │ │ │ ├── ColumnsComponent.tsx │ │ │ └── FeatureComponent.tsx │ ├── index.tsx │ └── PDFDocument.tsx ├── assets │ └── ezgif.com-video-to-gif.gif ├── zeusSelectionSets.ts ├── constants.ts ├── frontend-types.ts ├── utils.ts └── Colors.ts ├── .gitattributes ├── .vscode └── settings.json ├── sandbox ├── assets │ ├── favicon.ico │ └── index.html ├── webpack.production.config.js ├── index.tsx ├── tsconfig.json └── webpack.config.js ├── .npmignore ├── .gitlab-ci.yml ├── .gitignore ├── tsconfig.json ├── package.json ├── .eslintrc.json └── README.md /src/topmenu/components/Tooltip.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./screens"; 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.gif filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /src/screens/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./ReactPDFEditor"; 2 | export * from "./InitialPDFModels" -------------------------------------------------------------------------------- /sandbox/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/HEAD/sandbox/assets/favicon.ico -------------------------------------------------------------------------------- /src/models/index.ts: -------------------------------------------------------------------------------- 1 | import { getString } from "./getString"; 2 | 3 | export const translated = getString("en"); 4 | -------------------------------------------------------------------------------- /src/components/editor/styles/Column.tsx: -------------------------------------------------------------------------------- 1 | import { style } from "typestyle"; 2 | 3 | export const Main = style({ 4 | }); 5 | -------------------------------------------------------------------------------- /src/components/index.tsx: -------------------------------------------------------------------------------- 1 | import * as Icons from "./icons"; 2 | import * as Editor from "./editor"; 3 | export { Icons, Editor }; 4 | -------------------------------------------------------------------------------- /src/livepdf/fonts/FiraSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/HEAD/src/livepdf/fonts/FiraSans-Bold.ttf -------------------------------------------------------------------------------- /src/livepdf/fonts/FiraSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/HEAD/src/livepdf/fonts/FiraSans-Light.ttf -------------------------------------------------------------------------------- /src/livepdf/fonts/FiraSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/HEAD/src/livepdf/fonts/FiraSans-Medium.ttf -------------------------------------------------------------------------------- /src/livepdf/fonts/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aexol-studio/react-pdf-editor/HEAD/src/livepdf/fonts/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /src/components/atoms/index.tsx: -------------------------------------------------------------------------------- 1 | export { ButtonSimple } from "./ButtonSimple"; 2 | export { Checkbox } from "./Checkbox"; 3 | export { IconButton } from "./IconButton"; -------------------------------------------------------------------------------- /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/SmallInput.tsx: -------------------------------------------------------------------------------- 1 | import { style } from "typestyle"; 2 | export const Input = style({ 3 | $debugName: "TopMenuInput", 4 | width: 40 5 | }); 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /sandbox/webpack.production.config.js: -------------------------------------------------------------------------------- 1 | const { devServer, ...webpackParams } = require("./webpack.config"); 2 | 3 | module.exports = { 4 | ...webpackParams, 5 | mode: "production" 6 | }; 7 | -------------------------------------------------------------------------------- /.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__ -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/zeusSelectionSets.ts: -------------------------------------------------------------------------------- 1 | import { ZeusSelect, Style } from "./graphql-zeus"; 2 | 3 | export const StyleSelectionSet = ZeusSelect 22 | 23 | 24 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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": [ 17 | "es6", 18 | "es7", 19 | "esnext", 20 | "dom" 21 | ], 22 | "rootDir": "./src", 23 | "baseUrl": "./", 24 | "paths": { 25 | "@/*": [ 26 | "./src/*" 27 | ], 28 | "@components/*": [ 29 | "./src/components/*" 30 | ], 31 | "@styles/*": [ 32 | "./src/components/styles/*" 33 | ] 34 | }, 35 | }, 36 | "include": [ 37 | "next-env.d.ts", 38 | "**/*.ts", 39 | "**/*.tsx", 40 | "./src/**/*", 41 | "types.d.ts" 42 | ], 43 | "exclude": [ 44 | "lib", 45 | "node_modules", 46 | "sandbox" 47 | ] 48 | } -------------------------------------------------------------------------------- /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/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 |
23 | 37 |
38 |
39 | ); 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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