Skip to content

Commit

Permalink
UILivePreview component snippet wrapper (#3351)
Browse files Browse the repository at this point in the history
* UILivePreview component snippet wrapper

* hide iframe scrollbar

* livePreviewSource

* sortableGridList snippet fix

* fixed review notes
  • Loading branch information
adids1221 authored Nov 6, 2024
1 parent 5e4c08a commit 2e4d09a
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 41 deletions.
44 changes: 21 additions & 23 deletions docs/getting-started/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,30 @@ title: 'Usage'
# path: "/getting-started/usage"
---

import UILivePreview from '@site/src/components/UILivePreview';

This is a quick example of how to use our basic components, modifiers and presets to generate a good looking screen.
For detailed information please go over the other sections: [Style](../foundation/style.md), [Modifiers](../foundation/modifiers.md), Components...

<!-- ![basic showcase](basic-showcase.png). -->

```jsx live
function Example(props) {
return (
<div>
<View flex center>
<Text blue50 text20 marginB-s5>
Welcome
</Text>
<SegmentedControl segments={[{label: 'Register'}, {label: 'Login'}]} />

<View marginT-s5>
<TextField preset="outline" placeholder="username" />
<TextField preset="outline" placeholder="password" secureTextEntry grey10 />
</View>

<View row marginT-s5 centerV>
<Button link text70 orange30 label="Sign Up" marginR-s5 />
<Button text70 white background-orange30 label="Login" />
<UILivePreview code={`function Example(props) {
return (
<div>
<View flex center>
<Text blue50 text20 marginB-s5>
Welcome
</Text>
<SegmentedControl segments={[{label: 'Register'}, {label: 'Login'}]}/>
<View marginT-s5>
<TextField preset="outline" placeholder="username" />
<TextField preset="outline" placeholder="password" secureTextEntry grey10 />
</View>
<View row marginT-s5 centerV>
<Button link text70 orange30 label="Sign Up" marginR-s5 />
<Button text70 white background-orange30 label="Login" />
</View>
</View>
</View>
</div>
);
}
```
</div>
);
}`}/>
4 changes: 4 additions & 0 deletions docuilib/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const darkCodeTheme = themes.dracula;
projectName: 'react-native-ui-lib', // Usually your repo name.
trailingSlash: false,
customFields: {
livePreviewSource:
process.env.NODE_ENV === 'development'
? 'http://localhost:3000/react-native-ui-lib/livePreview'
: 'https://wix.github.io/react-native-ui-lib/livePreview',
docsMainEntry: 'getting-started/setup',
expoSnackLink: 'https://snack.expo.io/@ethanshar/rnuilib_snack',
stars: '4.7'
Expand Down
85 changes: 85 additions & 0 deletions docuilib/src/components/UILivePreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, {useEffect, useRef, useState, useMemo} from 'react';
import {StyleSheet} from 'react-native';
import {LiveProvider, LiveEditor} from 'react-live';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {View, Colors} from 'react-native-ui-lib/core';
import ReactLiveScope from '../theme/ReactLiveScope';

export const IFRAME_MESSAGE_TYPE = 'LIVE_PREVIEW_CODE_UPDATE_MESSAGE';

export default function UILivePreview({code: codeProp}) {
const [code, setCode] = useState(codeProp);
const [iframeLoaded, setIframeLoaded] = useState(false);
const {siteConfig} = useDocusaurusContext();
const iframeRef = useRef(null);
const iframeSource = siteConfig?.customFields?.livePreviewSource as string;

useEffect(() => {
if (iframeLoaded) {
sendMessageToIframe(code);
}
}, [iframeLoaded, code]);

const sendMessageToIframe = code => {
const message = {type: IFRAME_MESSAGE_TYPE, code};
iframeRef.current?.contentWindow.postMessage(message, '*');
};

const liveEditorStyle = useMemo(() => {
return {overflowY: 'scroll', scrollbarWidth: 'none'};
}, []);

return (
<View row gap-s2 style={styles.liveCodeWrapper}>
<LiveProvider code={code} scope={ReactLiveScope}>
<View flex style={styles.editorWrapper}>
<LiveEditor
className="font-mono"
onChange={setCode}
//@ts-ignore
style={liveEditorStyle}
/>
</View>
<View bg-$backgroundDefault margin-s2 style={styles.iframeWrapper}>
<iframe
ref={iframeRef}
style={styles.iframe}
src={iframeSource}
title="Simulator"
onLoad={() => setIframeLoaded(true)}
/>
</View>
</LiveProvider>
</View>
);
}

const styles = StyleSheet.create({
liveCodeWrapper: {
borderRadius: 20,
borderWidth: 1,
backgroundColor: '#011627',
height: 725,
width: 900
},
editorWrapper: {maxHeight: 700, padding: 10, borderRadius: 20, overflow: 'hidden'},
iframeWrapper: {
alignSelf: 'center',
overflow: 'hidden',
borderRadius: 40,
borderWidth: 4,
borderColor: Colors.$outlineDisabledHeavy,
width: 320,
height: 700
},
iframe: {
width: 335, // Slightly wider to hide scrollbar
height: '100%',
position: 'absolute',
top: 0,
left: 0,
border: 0,
padding: 10,
background: 'transparent'
}
});
29 changes: 29 additions & 0 deletions docuilib/src/pages/livePreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, {useEffect, useState} from 'react';
import {StyleSheet} from 'react-native';
import {LiveProvider, LivePreview} from 'react-live';
import ReactLiveScope from '../theme/ReactLiveScope';
import {IFRAME_MESSAGE_TYPE} from '@site/src/components/UILivePreview';

export default function UILivePreview() {
const [code, setCode] = useState(``);

useEffect(() => {
window.addEventListener('message', (e: MessageEvent) => {
if (e.data.type === IFRAME_MESSAGE_TYPE) {
setCode(e.data.code);
}
});
}, []);

return (
<LiveProvider code={code} scope={ReactLiveScope}>
<LivePreview style={styles.livePreview}/>
</LiveProvider>
);
}

const styles = StyleSheet.create({
livePreview: {
overflow: 'hidden'
}
});
2 changes: 2 additions & 0 deletions docuilib/src/theme/ReactLiveScope/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import SortableGridList from 'react-native-ui-lib/sortableGridList';
import SortableList from 'react-native-ui-lib/sortableList';
import Switch from 'react-native-ui-lib/switch';
import TextField from 'react-native-ui-lib/textField';
import UILivePreview from '@site/src/components/UILivePreview';
import * as Playground from './Playground';

Assets.loadAssetsGroup('icons.demo', {
Expand Down Expand Up @@ -56,6 +57,7 @@ const ReactLiveScope = {
/* Docs' utils and components */
Data,
Playground,
UILivePreview,
/* UI Lib's components */
ActionBar,
Assets,
Expand Down
14 changes: 7 additions & 7 deletions scripts/buildDocsCommon.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@ function resetDocsDir() {
function processComponents(components) {
/** Break into compound components (TabController.TabPage) and parent components (TabController) */
const compoundComponents = components.filter(c => c.name.includes('.'));
const parentComponents = _.flow(
components => _.map(components, c => c.name.split('.')[0]),
_.uniq
)(compoundComponents);
const parentComponents = _.flow(components => _.map(components, c => c.name.split('.')[0]),
_.uniq)(compoundComponents);

components.forEach(component => {
const [componentName, componentParentName] = getComponentNameParts(component.name);
Expand Down Expand Up @@ -114,6 +112,7 @@ function generateExtendsLink(extendsLink) {

function buildOldDocs(component) {
let content = '';
content += `import UILivePreview from '@site/src/components/UILivePreview';\n\n`;

/* General Info */
content += `${component.description} \n`;
Expand Down Expand Up @@ -170,9 +169,10 @@ function buildOldDocs(component) {
/* Snippet */
if (component.snippet) {
content += `### Usage\n`;
content += '``` jsx live\n';
content += component.snippet?.map(item => _.replace(item, new RegExp(/\$[1-9]/, 'g'), '')).join('\n');
content += '\n```\n';
content += `<UILivePreview code={\`${component.snippet
?.map(item => _.replace(item, new RegExp(/\$[1-9]/, 'g'), ''))
.join('\n')
.toString()}\`}/>\n\n`;
} else {
console.warn(`${component.name} does not have a snippet`);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/marquee/marquee.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
],
"snippet": [
"<Marquee",
" key={`${index}`$1}",
" key={\\`\\${index}\\`$1}",
" label={'Hey there'$2}",
" direction={directionHorizontal ? MarqueeDirections.LEFT : MarqueeDirections.RIGHT$3}",
" duration={duration$4}",
Expand Down
5 changes: 1 addition & 4 deletions src/components/sortableGridList/sortableGridList.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@
" };",
"",
" return (",
" <div>",
" <View style={{width: 800}}>",
" <View flex>",
" <Playground.ToggleControl title={'Order by index'} state={shouldOrderByIndex} setState={setShouldOrderByIndex}/>",
" <SortableGridList",
" data={data$1}",
Expand All @@ -69,11 +68,9 @@
" renderItem={renderItem$4}",
" itemSpacing={Spacings.s3$5}",
" listPadding={Spacings.s5$6}",
" containerWidth={800$7}",
" orderByIndex={shouldOrderByIndex$8}",
" />",
" </View>",
" </div>",
" );",
"}"
]
Expand Down
6 changes: 3 additions & 3 deletions src/components/sortableList/SortableList.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@
"snippet": [
"function Example(props) {",
" const data = Array.from({length: 10}, (_, index) => {",
" let text = `${index}`;",
" let text = \\`\\${index}\\`;",
" if (index === 3) {",
" text = 'Locked item';",
" }",
" ",
" return {",
" text,",
" id: `${index}`,",
" id: \\`\\${index}\\`,",
" locked: index === 3",
" };",
" });",
Expand All @@ -78,7 +78,7 @@
" }, []);",
"",
" const keyExtractor = useCallback((item: Item) => {",
" return `${item.id}`;",
" return \\`\\${item.id}\\`;",
" }, []);",
"",
" return (",
Expand Down
10 changes: 8 additions & 2 deletions src/components/touchableOpacity/touchableOpacity.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@
},
{"name": "style", "type": "ViewStyle", "description": "Custom style"},
{"name": "recorderTag", "type": "'mask' | 'unmask'", "description": "Recorder Tag"},
{"name": "onPress", "type": "(props?: TouchableOpacityProps & {event: GestureResponderEvent} | any) => void", "description": "On press callback"}
{
"name": "onPress",
"type": "(props?: TouchableOpacityProps & {event: GestureResponderEvent} | any) => void",
"description": "On press callback"
}
],
"snippet": [
"<TouchableOpacity onPress={() => console.log('pressed')$1}><Text text40>Click Me!</Text></TouchableOpacity>"
"<TouchableOpacity onPress={() => console.log('pressed')$1}>",
" <Text text40>Click Me!</Text>",
"</TouchableOpacity>"
]
}
2 changes: 1 addition & 1 deletion src/incubator/Slider/slider.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
" value={0$1}",
" minimumValue={0$2}",
" maximumValue={10$3}",
" onValueChange={value => console.log(`value changed: ${value}`)$4}",
" onValueChange={value => console.log(\\`value changed: \\${value}\\`)$4}",
"/>"
]
}

0 comments on commit 2e4d09a

Please sign in to comment.