Skip to content

Commit

Permalink
Merge pull request #232 from rnbwdev/dev
Browse files Browse the repository at this point in the history
merge dev
  • Loading branch information
sbsoso0411 authored Jul 11, 2023
2 parents 153336d + fd87e2a commit 5f58e19
Show file tree
Hide file tree
Showing 10 changed files with 428 additions and 185 deletions.
62 changes: 60 additions & 2 deletions src/_node/html/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ import {
THtmlReferenceData,
} from './types';

const noNeedClosingTag = ['area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'link',
'meta',
'param',
'source',
'track',
'wbr']
const emptyImage = window.location.origin + "/images/empty-image.svg"
export const setHtmlNodeInAppAttribName = (node: TNode, newUid: TNodeUid) => {
const nodeData = node.data as THtmlNodeData
Expand Down Expand Up @@ -308,7 +322,7 @@ export const serializeHtml = (tree: TNodeTreeData, htmlReferenceData: THtmlRefer
nodeHtmlInApp = ``
} else if (nodeData.type === 'text') {
// replace "<" or ">" to "&lt;" and "&gt;", only in app
nodeHtml = nodeData.data.replace(/</g, `&lt;`).replace(/>/g, `&gt;`)
nodeHtml = nodeData.data
nodeHtmlInApp = nodeData.data.replace(/</g, `&lt;`).replace(/>/g, `&gt;`)
} else if (nodeData.type === 'script' || nodeData.type === 'style') {
nodeHtml = `<${nodeData.type}${attribsHtml}>${childrenHtml}</${nodeData.type}>`
Expand Down Expand Up @@ -393,7 +407,12 @@ export const parseHtmlCodePart = (content: string, htmlReferenceData: THtmlRefer
ReactHtmlParser(content, {
decodeEntities: true,
transform: (node, index, transform) => {
node.valid = true
if ((node as THtmlDomNodeData).type === 'tag' && (htmlReferenceData.elements[(node as THtmlDomNodeData).name] || (node as THtmlDomNodeData).name.indexOf('-') !== -1)) {
node.valid = true
}
else{
node.valid = false
}
},
preprocessNodes: (nodes: THtmlDomNodeData[]) => {
// build root node
Expand Down Expand Up @@ -496,4 +515,43 @@ export const parseHtmlCodePart = (content: string, htmlReferenceData: THtmlRefer
const { html: formattedContent } = serializeHtml(tree, htmlReferenceData, osType)

return { formattedContent, contentInApp: '', tree, nodeMaxUid: String(_nodeMaxUid) as TNodeUid }
}

export const checkValidHtml = (content: string): boolean => {
// remove code & pre & script tag's content
const tmpString = content.replace(/<pre\b[^<]*(?:(?!<\/pre>)<[^<]*)*<\/pre>/gi, '<pre></pre>').replace(/<code\b[^<]*(?:(?!<\/code>)<[^<]*)*<\/code>/gi, '<code></code>').replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '<script></script>').replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '<style></style>');
let hasMismatchedTags = false
let openingTags = [];
let closingTags = [];
let regex = /<\/?[a-zA-Z0-9]+\b[^>]*>/g; // Matches any HTML tag
let match = regex.exec(tmpString);
while (match) {
let tag = match[0];
if (tag.startsWith('</')) {
let _tag = tag.slice(2, -1).split(' ')[0]
if (noNeedClosingTag.find(_item => _tag === _item) === undefined) {
closingTags.push(_tag);
}
} else {
let _tag = tag.slice(1, -1).split(' ')[0]
if (noNeedClosingTag.find(_item => _tag === _item) === undefined) {
openingTags.push(_tag);
}
}
match = regex.exec(tmpString);
}

if (openingTags.length !== closingTags.length) {
hasMismatchedTags = true; // Different number of opening and closing tags
}
else {
openingTags.sort()
closingTags.sort()
for (let i = 0 ; i < openingTags.length ; i ++) {
if (openingTags[i] !== closingTags[i])
hasMismatchedTags = true
}
}

return hasMismatchedTags
}
7 changes: 1 addition & 6 deletions src/_ref/cmdk.ref/Actions.csv
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
Name,Icon,Description,Keyboard Shortcut,Group,Context
Add,plus,Add something,a,Project,all
Code,code-html,Open Code,c,Project,all
Run,play,"""Run"" (Preview)",r,Project,all
Save,data,Save current file,cmd+s,Project,all
Design,edit,"""Design"" On/Off (hide panels)",\,Project,all
Publish,cloud-upload,,p,Project,all
Share,share,,h,Project,all
Download,download,,cmd+d,Project,all
Cut,copy,,cmd+x,Basic,"file, html"
Copy,copy,,cmd+c,Basic,"file, html"
Expand All @@ -14,8 +11,6 @@ Delete,cross,,backspace,Basic,"file, html"
Duplicate,copy,,cmd+j,Basic,"file, html"
Group,border,,cmd+g,Basic,html
Ungroup,position,,cmd+shift+g,Basic,html
Style,brush,Add Style,s,Basic,html
Text,text,Edit Text,t,Basic,html
Undo,undo,,cmd+z,Basic,all
Redo,redo,,cmd+shift+z,Basic,all
Turn into,sync,,,Other,html
Redo,redo,,cmd+shift+z,Basic,all
85 changes: 36 additions & 49 deletions src/components/main/actionsPanel/navigatorPanel/NavigatorPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,28 +114,28 @@ export default function NavigatorPanel(props: NavigatorPanelProps) {
setFaviconFallback(false)
// set favicons of the workspace
if (file.uid === `${RootNodeUid}/index.html`) {
if (validNodeTree) {
let hasFavicon = false
for (const x in validNodeTree) {
const nodeData = validNodeTree[x].data as THtmlNodeData
if (nodeData && nodeData.type === 'tag' && nodeData.name === 'link' && nodeData.attribs.rel === 'icon') {
if (nodeData.attribs.href.startsWith('http') || nodeData.attribs.href.startsWith('//')) {
setFavicon(nodeData.attribs.href)
}
else{
setFavicon(window.location.origin + '/rnbw/' + project.name + '/' + nodeData.attribs.href)
}
hasFavicon = true
}
}
// if (validNodeTree) {
// let hasFavicon = false
// for (const x in validNodeTree) {
// const nodeData = validNodeTree[x].data as THtmlNodeData
// if (nodeData && nodeData.type === 'tag' && nodeData.name === 'link' && nodeData.attribs.rel === 'icon') {
// if (nodeData.attribs.href.startsWith('http') || nodeData.attribs.href.startsWith('//')) {
// setFavicon(nodeData.attribs.href)
// }
// else{
// setFavicon(window.location.origin + '/rnbw/' + project.name + '/' + nodeData.attribs.href)
// }
// hasFavicon = true
// }
// }

if (!hasFavicon) {
setFavicon('')
}
}
else{
setFavicon('')
}
// if (!hasFavicon) {
// setFavicon('')
// }
// }
// else{
// setFavicon('')
// }

let hasFavicon = false
for (const x in validNodeTree) {
Expand Down Expand Up @@ -252,26 +252,15 @@ export default function NavigatorPanel(props: NavigatorPanelProps) {
return file.uid !== '' ? <>
<div
id="NavigatorPanel"
style={{
position: 'relative',
top: 0,
left: 0,
width: '100%',

overflow: 'auto',

display: 'flex',
alignItems: 'center',
}}
className='padding-s border-bottom gap-s'
className='padding-m border-bottom gap-s'
onClick={onPanelClick}
ref={navigatorPanelRef}
>
{!navigatorDropDownType ?
<>
{/* workspace */}
{/* <>
<div style={{'minWidth': '24px'}} className="radius-m icon-s align-center " onClick={onWorkspaceClick}>
<div onClick={onWorkspaceClick}>
<img className='icon-s' src={unsavedProject ? (theme === 'Light' ? unsavedLightProjectImg : unsavedDarkProjectImg) : (theme === 'Light' ? projectLightImg : projectDarkImg)}></img>
</div>
</> */}
Expand All @@ -280,12 +269,10 @@ export default function NavigatorPanel(props: NavigatorPanelProps) {
{/* project */}
<>
<div className="gap-s align-center" onClick={onProjectClick}>
<div className="radius-m icon-s align-center">
{favicon === null || favicon === "" || faviconFallback ?
<SVGIconI {...{ "class": "icon-xs" }}>folder</SVGIconI> :
<img className='icon-s' onError={handleImageError} style={{'width': '18px', 'height' : '18px'}} src={project.context === 'idb' ? 'https://rnbw.company/images/favicon.png' : favicon}></img>
}
</div>
{/* {favicon === null || favicon === "" || faviconFallback ? */}
<SVGIconI {...{ "class": "icon-xs" }}>folder</SVGIconI>
{/* <img className='icon-s' onError={handleImageError} style={{'width': '18px', 'height' : '18px'}} src={project.context === 'idb' ? 'https://rnbw.company/images/favicon.png' : favicon}></img> */}
{/* } */}
<span className="text-s">{project.name}</span>
</div>
</>
Expand All @@ -311,7 +298,7 @@ export default function NavigatorPanel(props: NavigatorPanelProps) {
<>
{/* workspace */}
{/* <>
<div style={{'minWidth': '24px'}} className="radius-m icon-s align-center " onClick={onWorkspaceClick}>
<div onClick={onWorkspaceClick}>
<img className='icon-s' src={unsavedProject ? (theme === 'Light' ? unsavedLightProjectImg : unsavedDarkProjectImg) : (theme === 'Light' ? projectLightImg : projectDarkImg)}></img>
</div>
</> */}
Expand All @@ -320,7 +307,7 @@ export default function NavigatorPanel(props: NavigatorPanelProps) {
<>
{/* workspace */}
{/* <>
<div className="radius-m icon-s align-center " onClick={onWorkspaceClick}>
<div onClick={onWorkspaceClick}>
<img className='icon-s' src={unsavedProject ? (theme === 'Light' ? unsavedLightProjectImg : unsavedDarkProjectImg) : (theme === 'Light' ? projectLightImg : projectDarkImg)}></img>
</div>
</>
Expand All @@ -329,12 +316,12 @@ export default function NavigatorPanel(props: NavigatorPanelProps) {
{/* project */}
<>
<div className="gap-s align-center" onClick={onProjectClick}>
<div className="radius-m icon-s align-center">
{favicon === null || favicon === "" || faviconFallback ?
<SVGIconI {...{ "class": "icon-xs" }}>folder</SVGIconI> :
<img className='icon-s' onError={handleImageError} style={{'width': '18px', 'height' : '18px'}} src={project.context === 'idb' ? 'https://rnbw.company/images/favicon.png' : favicon}></img>
}
</div>

{/* {favicon === null || favicon === "" || faviconFallback ? */}
<SVGIconI {...{ "class": "icon-xs" }}>folder</SVGIconI>
{/* <img className='icon-s' onError={handleImageError} style={{'width': '18px', 'height' : '18px'}} src={project.context === 'idb' ? 'https://rnbw.company/images/favicon.png' : favicon}></img> */}
{/* } */}

<span className="text-s">{project.name}</span>
</div>
</>
Expand Down Expand Up @@ -381,7 +368,7 @@ export default function NavigatorPanel(props: NavigatorPanelProps) {
onOpenProject(_project)
}}>
<div className="gap-s align-center">
<div className="navigator-project-item-icon radius-m icon-s align-center">
<div className="navigator-project-item-icon">
{_project.favicon ? <img className='icon-s' style={{'borderRadius': '50%'}} src={_project.favicon}></img> : <SVGIcon {...{ "class": "icon-xs" }}>folder</SVGIcon>}
</div>
<span className="navigator-project-item-name text-s">{_project.name}</span>
Expand Down
60 changes: 60 additions & 0 deletions src/components/main/actionsPanel/nodeTreeView/NodeTreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
} from '@_node/types';
import {
collapseFNNode,
expandFFNode,
expandFNNode,
fnSelector,
focusFNNode,
Expand Down Expand Up @@ -86,6 +87,7 @@ export default function NodeTreeView(props: NodeTreeViewProps) {
ffHandlers, setFFHandlers,
ffHoveredItem, setFFHoveredItem,
isHms, setIsHms,
setInitialFileToOpen,
ffAction, setFFAction,
currentFileUid, setCurrentFileUid,
// node tree view
Expand Down Expand Up @@ -656,6 +658,60 @@ export default function NodeTreeView(props: NodeTreeViewProps) {
navigatorDropDownType !== null && setNavigatorDropDownType(null)
}, [navigatorDropDownType])

// ------------------------------------------------------------- open wc -------------------------------------------------------------
const openWebComponent = useCallback((item: TNodeUid) => {
// check the element is wc
const nodeData = validNodeTree[item].data as THtmlNodeData
let exist = false
if (nodeData && htmlReferenceData.elements[nodeData.name] === undefined && nodeData.type === 'tag') {
const wcName = nodeData.name
for (let x in ffTree) {
const defineRegex = /customElements\.define\(\s*['"]([\w-]+)['"]/;
if ((ffTree[x].data as TFileNodeData).content && (ffTree[x].data as TFileNodeData).ext === '.js') {
const match = ((ffTree[x].data as TFileNodeData).content).match(defineRegex);
if (match) {
// check web component
if (wcName === match[1].toLowerCase()) {
const fileName = (ffTree[x].data as TFileNodeData).name
let src = ''
for (let i in validNodeTree) {
if ((validNodeTree[i].data as THtmlNodeData).type === 'script' && (validNodeTree[i].data as THtmlNodeData).html.search(fileName + '.js') !== -1) {
src = (validNodeTree[i].data as THtmlNodeData).attribs.src
break
}
}
if (src !== '') {
if (src.startsWith('http') || src.startsWith('//')) {
alert('rnbw couldn\'t find it\'s source file')
break
}
else{
setInitialFileToOpen(ffTree[x].uid)
setNavigatorDropDownType('project')
// expand path to the uid
const _expandedItems: string[] = []
let _file = ffTree[x]
while (_file && _file.uid !== RootNodeUid) {
_file = ffTree[_file.parentUid as string]
if (_file && !_file.isEntity && (!expandedItemsObj[_file.uid] || expandedItemsObj[_file.uid] === undefined))
_expandedItems.push(_file.uid)
}
dispatch(expandFFNode(_expandedItems))
exist = true
break
}
}
}
}
}
}

if (!exist) {
alert('rnbw couldn\'t find it\'s source file')
}
}
}, [htmlReferenceData, validNodeTree, ffTree])

const isDragging = useRef<boolean>(false)

return useMemo(() => {
Expand Down Expand Up @@ -754,6 +810,10 @@ export default function NodeTreeView(props: NodeTreeViewProps) {

navigatorDropDownType !== null && setNavigatorDropDownType(null)
}}
onDoubleClick={(e: React.MouseEvent) => {
e.stopPropagation()
openWebComponent(props.item.index as TNodeUid)
}}
onMouseEnter={(e) => {
const ele = e.target as HTMLElement
let _uid: TNodeUid | null = ele.getAttribute('id')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1937,7 +1937,7 @@ export default function WorkspaceTreeView(props: WorkspaceTreeViewProps) {

...(navigatorDropDownType ? { zIndex: 2 } : {})
}}
className={navigatorDropDownType ? 'border-left border-right border-bottom shadow background-primary' : ''}
className={navigatorDropDownType ? 'border-bottom background-primary' : ''}
onClick={onPanelClick}
>
<TreeView
Expand Down
Loading

0 comments on commit 5f58e19

Please sign in to comment.