Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: react components package, QR code component #134

Merged
merged 2 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/react-components/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @type { import('@storybook/react-webpack5').StorybookConfig } */
const config = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/preset-create-react-app',
'@storybook/addon-onboarding',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
docs: {
autodocs: 'tag',
},
staticDirs: ['../public'],
};
export default config;
14 changes: 14 additions & 0 deletions packages/react-components/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** @type { import('@storybook/react').Preview } */
const preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};

export default preview;
16 changes: 16 additions & 0 deletions packages/react-components/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# React Components

The React Components package contains a set of React components that can be used to build a web application.

## Available Scripts

In the project directory, you can run:

### `pnpm start`

Runs the example app that render the components in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.

### `pnpm storybook`

Runs the storybook that shows the components in an isolated environment.\
3 changes: 3 additions & 0 deletions packages/react-components/babel.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'],
};
71 changes: 71 additions & 0 deletions packages/react-components/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "@vckit/react-components",
"version": "1.0.0-beta.5",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"type": "module",
"scripts": {
"start": "react-scripts start",
"build": "tsc",
"build:watch": "tsc -b --watch",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest",
"plugin:storybook/recommended"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"dependencies": {
"qrcode": "^1.5.3"
},
"devDependencies": {
"@babel/preset-react": "^7.7.0",
"@babel/preset-typescript": "^7.22.5",
"@storybook/addon-essentials": "^7.1.0-rc.2",
"@storybook/addon-interactions": "^7.1.0-rc.2",
"@storybook/addon-links": "^7.1.0-rc.2",
"@storybook/addon-onboarding": "^1.0.7",
"@storybook/blocks": "^7.1.0-rc.2",
"@storybook/preset-create-react-app": "^7.1.0-rc.2",
"@storybook/react": "^7.1.0-rc.2",
"@storybook/react-webpack5": "^7.1.0-rc.2",
"@storybook/testing-library": "^0.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/qrcode": "^1.5.1",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"babel-plugin-named-exports-order": "^0.0.2",
"eslint-plugin-storybook": "^0.6.12",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"storybook": "^7.1.0-rc.2",
"tslib": "^2.6.0",
"typescript": "^5.1.6",
"web-vitals": "^2.1.4",
"webpack": "^5.88.1"
}
}
Binary file added packages/react-components/public/favicon.ico
Binary file not shown.
28 changes: 28 additions & 0 deletions packages/react-components/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<title>React Components</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
38 changes: 38 additions & 0 deletions packages/react-components/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.App {
text-align: center;
}

.App-logo {
height: 40vmin;
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}

.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
23 changes: 23 additions & 0 deletions packages/react-components/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import './App.css';
import { QrCodeDocumentWrapper } from './components/QrCodeDocumentWrapper';

/**
* The App component is the entry point of the application. It is responsible for render the components as the demo page.
*
*/

function App() {
const qrCodeValue =
'http://localhost:4200/verify?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3332%2Fencrypted-storage%2Fencrypted-data%2Ff0754961-d831-411e-8596-786bf9a01aa2%22%2C%22key%22%3A%22ec6065ccf59c8cf466b23ca666813de69238c1f54eff927fa6f04316e55a3fd3%22%7D%7D';
const doc = '<h1>hello world</h1>';

return (
<div className="App">
<QrCodeDocumentWrapper qrCodeValue={qrCodeValue}>
<div dangerouslySetInnerHTML={{ __html: doc }}></div>
</QrCodeDocumentWrapper>
</div>
);
}

export default App;
28 changes: 28 additions & 0 deletions packages/react-components/src/components/QrCode/QrCode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useEffect, useRef } from 'react';
import * as QrCode from 'qrcode';

interface QRCodeProps {
value: string;
qrCodeOptions?: QrCode.QRCodeRenderersOptions;
}

export function QRCode({ value, qrCodeOptions = { width: 200 } }: QRCodeProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);

useEffect(() => {
if (value) {
QrCode.toCanvas(
canvasRef.current,
value,
{ ...qrCodeOptions },
function (error) {
if (error) {
throw new Error('Error generating the QRCode');
}
}
);
}
}, [value, qrCodeOptions]);

return <canvas ref={canvasRef}></canvas>;
}
1 change: 1 addition & 0 deletions packages/react-components/src/components/QrCode/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { QRCode } from './QrCode.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { CSSProperties, useCallback, useState } from 'react';
import { useDrag, useDrop } from 'react-dnd';
import type { XYCoord } from 'react-dnd';
import { QRCode } from '../QrCode/QrCode.js';

const styles: { [key: string]: CSSProperties } = {
renderContainer: {
margin: 'auto',
border: '1px solid black',
position: 'relative',
},
qrCode: {
position: 'absolute',
padding: '0.5rem 1rem',
cursor: 'move',
},
};

interface DragItem {
left: number;
top: number;
}

const ItemTypes = {
QRCODE: 'QRCode',
};

export interface QrCodeDocumentContainerProps {
qrCodeValue: string;
width?: string;
height?: string;
children?: React.ReactNode;
}

export function QrCodeDocumentContainer({
qrCodeValue,
width,
height,
children,
}: QrCodeDocumentContainerProps) {
width = width || '595px';
height = height || '842px';

const [qrcode, setQrcode] = useState<{
top: number;
left: number;
}>({ top: 20, left: 80 });

const moveQRCode = useCallback(
(left: number, top: number) => {
setQrcode((qrcode) => {
return { ...qrcode, left, top };
});
},
[setQrcode]
);

const [, drop] = useDrop(
() => ({
accept: ItemTypes.QRCODE,
drop(item: DragItem, monitor) {
const delta = monitor.getDifferenceFromInitialOffset() as XYCoord;
const left = Math.round(item.left + delta.x);
const top = Math.round(item.top + delta.y);
moveQRCode(left, top);
return undefined;
},
}),
[setQrcode]
);

const [{ isDragging }, drag] = useDrag(
() => ({
type: ItemTypes.QRCODE,
item: { left: qrcode.left, top: qrcode.top },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
}),
[qrcode.left, qrcode.top]
);

const drapableQRCode = () => {
if (!isDragging && qrCodeValue) {
return (
<div
ref={drag}
style={{ ...styles.qrCode, left: qrcode.left, top: qrcode.top }}
>
<QRCode value={qrCodeValue} />
</div>
);
}
};

return (
<div style={{ ...styles.renderContainer, width, height }} ref={drop}>
{drapableQRCode()}

{children}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider } from 'react-dnd';
import {
QrCodeDocumentContainer,
QrCodeDocumentContainerProps,
} from './QrCodeDocumentContainer.js';

export function QrCodeDocumentWrapper({
qrCodeValue,
children,
}: QrCodeDocumentContainerProps) {
return (
<DndProvider backend={HTML5Backend}>
<QrCodeDocumentContainer qrCodeValue={qrCodeValue}>
{children}
</QrCodeDocumentContainer>
</DndProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { QrCodeDocumentWrapper } from './QrCodeDocumentWrapper.js';
13 changes: 13 additions & 0 deletions packages/react-components/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
1 change: 1 addition & 0 deletions packages/react-components/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './components/QrCodeDocumentWrapper/QrCodeDocumentWrapper.js';
Loading