Skip to content

Commit

Permalink
Feature/MN-80/modify-svg-dynamically (#496)
Browse files Browse the repository at this point in the history
Add custom styling to SVG overlays

---------

Co-authored-by: souyahia-monk <[email protected]>
  • Loading branch information
rohitsharma120582 and souyahia-monk authored May 26, 2023
1 parent 12edd24 commit 1e71e67
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 16 deletions.
2 changes: 2 additions & 0 deletions .idea/monkjs.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 16 additions & 6 deletions packages/camera/src/components/Capture/capture.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { MonitoringStatus, SentryOperation, SentryTag, SentryTransaction, useMonitoring } from '@monkvision/corejs';
import { utils } from '@monkvision/toolkit';
import { useMonitoring, MonitoringStatus, SentryTransaction, SentryOperation, SentryTag } from '@monkvision/corejs';
import PropTypes from 'prop-types';
import React, { forwardRef, useCallback, useEffect, useRef, useImperativeHandle, useMemo, useState } from 'react';
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';

import { useTranslation } from 'react-i18next';
import { ActivityIndicator, Platform, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
import { useMediaQuery } from 'react-responsive';
import Constants from '../../const';
import useEventStorage from '../../hooks/useEventStorage';
import log from '../../utils/log';

import AddDamageModal from '../AddDamageModal';
import AddDamageOverlay from '../AddDamageOverlay';

import AddDamageHelpModal from '../AddDamageModal/AddDamageHelpModal';
import useEventStorage from '../../hooks/useEventStorage';
import AddDamageOverlay from '../AddDamageOverlay';
import Camera from '../Camera';
import CloseEarlyConfirmModal from '../CloseEarlyConfirmModal';
import Controls from '../Controls';
Expand All @@ -30,7 +30,8 @@ import {
useSetPictureAsync,
useStartUploadAsync,
useTakePictureAsync,
useTitle, useUploadAdditionalDamage,
useTitle,
useUploadAdditionalDamage,
} from './hooks';

const AddDamageStatus = {
Expand Down Expand Up @@ -126,6 +127,7 @@ const Capture = forwardRef(({
onStartUploadPicture,
onFinishUploadPicture,
orientationBlockerProps,
overlayPathStyles,
primaryColor,
sightsContainerStyle,
style,
Expand Down Expand Up @@ -573,6 +575,12 @@ const Capture = forwardRef(({
<Overlay
svg={overlay}
style={[styles.overlay, overlaySize]}
rootStyles={{
position: 'fixed',
height: '95vh',
top: '2.5vh',
}}
pathStyles={overlayPathStyles}
/>
) : null}
{(isReady && overlay && loading === false
Expand All @@ -598,7 +606,7 @@ const Capture = forwardRef(({
</View>
) : null}
</>
), [isReady, loading, overlay, overlaySize, primaryColor, addDamageStatus]);
), [isReady, loading, overlay, overlaySize, primaryColor, addDamageStatus, overlayPathStyles]);

if (enableComplianceCheck && (endTour || (tourHasFinished && complianceHasFulfilledAll))) {
return (
Expand Down Expand Up @@ -804,6 +812,7 @@ Capture.propTypes = {
onUploadsChange: PropTypes.func,
onWarningMessage: PropTypes.func,
orientationBlockerProps: PropTypes.shape({ title: PropTypes.string }),
overlayPathStyles: PropTypes.object,
primaryColor: PropTypes.string,
resolutionOptions: PropTypes.shape({
QHDDelay: PropTypes.number,
Expand Down Expand Up @@ -916,6 +925,7 @@ Capture.defaultProps = {
onReady: () => {},
onStartUploadPicture: () => {},
orientationBlockerProps: null,
overlayPathStyles: {},
primaryColor: '#FFF',
resolutionOptions: undefined,
sightsContainerStyle: {},
Expand Down
54 changes: 54 additions & 0 deletions packages/camera/src/components/Overlay/SVGElementMapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint-disable react/no-array-index-key */
import PropTypes from 'prop-types';
import React, { useMemo } from 'react';

import { useInnerHTML, useJSXTransformAttributes, useCustomSVGAttributes } from './hooks';

export default function SVGElementMapper({
element,
rootStyles,
pathStyles,
}) {
const Tag = useMemo(() => element.tagName, [element]);
const innerHTML = useInnerHTML({ element });
const transformedAttributes = useJSXTransformAttributes(element);
const customAttributes = useCustomSVGAttributes({
element,
rootStyles,
pathStyles,
});
const attributes = useMemo(() => ({
...transformedAttributes,
...customAttributes,
style: {
...transformedAttributes?.style ?? {},
...customAttributes?.style ?? {},
},
}), []);
const children = useMemo(() => [...element.children], [element]);

return (
<Tag {...attributes}>
{innerHTML}
{children.map((child, id) => (
<SVGElementMapper
key={id.toString()}
element={child}
rootStyles={rootStyles}
pathStyles={pathStyles}
/>
))}
</Tag>
);
}

SVGElementMapper.propTypes = {
element: PropTypes.any.isRequired,
pathStyles: PropTypes.object,
rootStyles: PropTypes.object,
};

SVGElementMapper.defaultProps = {
pathStyles: {},
rootStyles: {},
};
4 changes: 4 additions & 0 deletions packages/camera/src/components/Overlay/hooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as useInnerHTML } from './useInnerHTML';
export { default as useJSXTransformAttributes } from './useJSXTransformAttributes';
export { default as useCustomSVGAttributes } from './useCustomSVGAttributes';
export { default as useXMLParser } from './useXMLParser';
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useMemo } from 'react';

export default function useCustomSVGAttributes({
element,
rootStyles,
pathStyles,
}) {
return useMemo(() => {
const elementTag = element.tagName;

if (elementTag === 'svg') {
return { style: rootStyles ?? {} };
}

return { style: pathStyles ?? {} };
}, [
element,
rootStyles,
pathStyles,
]);
}
11 changes: 11 additions & 0 deletions packages/camera/src/components/Overlay/hooks/useInnerHTML.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useMemo } from 'react';

export default function useInnerHTML({ element }) {
return useMemo(() => {
if (element.tagName === 'style' && !!element.innerHTML) {
return element.innerHTML;
}

return null;
}, [element]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useMemo } from 'react';

function tranformJsxAttribute(key, value) {
switch (key) {
case 'class':
return { key: 'className', value };
case 'xml:space':
return { key: 'xmlSpace', value };
case 'style':
return value.split(';')
.map((style) => style.split(':'))
.reduce((prev, curr) => {
const [styleKey, styleValue] = curr;
const transformedKey = styleKey.replace(/-./g, (css) => css.toUpperCase()[1]);
return {
...prev,
[transformedKey]: styleValue,
};
}, {});
default:
return { key, value };
}
}

export default function useJSXTransformAttributes(element) {
return useMemo(() => element
.getAttributeNames()
.reduce((prev, attr) => {
const { key, value } = tranformJsxAttribute(attr, element.getAttribute(attr));
return {
...prev,
[key]: value,
};
}, {}), [element]);
}
5 changes: 5 additions & 0 deletions packages/camera/src/components/Overlay/hooks/useXMLParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useMemo } from 'react';

export default function useXMLParser(xml) {
return useMemo(() => new DOMParser().parseFromString(xml, 'text/xml'), [xml]);
}
31 changes: 21 additions & 10 deletions packages/camera/src/components/Overlay/index.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
import React, { useEffect, useMemo } from 'react';
import PropTypes from 'prop-types';
import { Image } from 'react-native';
import React, { useEffect, useMemo } from 'react';
import log from '../../utils/log';

export default function Overlay({ label, svg, ...passThoughProps }) {
const base64 = useMemo(() => btoa(unescape(encodeURIComponent(svg))), [svg]);
import { useXMLParser } from './hooks';
import SVGElementMapper from './SVGElementMapper';

export default function Overlay({ label, svg, rootStyles, pathStyles }) {
const doc = useXMLParser(svg);
const svgElement = useMemo(() => {
const svgElm = doc.children[0];
if (svgElm.tagName !== 'svg') {
throw new Error('Invalid Overlay SVG: expected <svg> tag as the first children of XML document.');
}
return svgElm;
}, [doc]);

useEffect(() => {
log(['[Event] Loading sight', label]);
}, [label]);

return (
<Image
accessibilityLabel={`Overlay ${label}`}
source={{ uri: `data:image/svg+xml;base64,${base64}` }}
width="100%"
height="100%"
{...passThoughProps}
<SVGElementMapper
element={svgElement}
rootStyles={rootStyles}
pathStyles={pathStyles}
/>
);
}

Overlay.propTypes = {
label: PropTypes.string,
pathStyles: PropTypes.object,
rootStyles: PropTypes.object,
svg: PropTypes.string.isRequired,
};

Overlay.defaultProps = {
label: '',
pathStyles: {},
rootStyles: {},
};

0 comments on commit 1e71e67

Please sign in to comment.