',
shortcut: ['KeyI', 'KeyB'],
section: sections.general,
perform: () =>
selectionState.type === 'elementSelected' &&
insertBeforeNode(
selectionState.selectedNode,
t.jsxElement(
t.jsxOpeningElement(t.jsxIdentifier('div'), []),
t.jsxClosingElement(t.jsxIdentifier('div')),
[],
),
),
},
insertDivAfter: {
showIf: selectionState.type === 'elementSelected',
name: 'Insert after:
',
shortcut: ['KeyI', 'KeyA'],
section: sections.general,
perform: () =>
selectionState.type === 'elementSelected' &&
insertAfterNode(
selectionState.selectedNode,
t.jsxElement(
t.jsxOpeningElement(t.jsxIdentifier('div'), []),
t.jsxClosingElement(t.jsxIdentifier('div')),
[],
),
),
},
...(selectionState.type === 'elementSelected' && selectionState.selectedNode instanceof Element
? Object.fromEntries(
htmlTags
.filter((tagName) => tagName !== (selectionState.selectedNode as Element).tagName.toLowerCase())
.map((tagName) => [
`changeTag-${tagName}`,
{
name: `<${tagName}>`,
shortcut: [],
section: sections.changeTag,
perform: () =>
selectionState.type === 'elementSelected' &&
changeTag(selectionState.selectedNode as Element, tagName),
},
]),
)
: {}),
insertTextChild: {
showIf:
searchQuery !== '' &&
selectionState.type === 'elementSelected' &&
selectionState.selectedNode instanceof Element,
section: sections.insertText,
name: `Insert child: ${searchQuery}`,
shortcut: [],
perform: () => {
if (selectionState.type !== 'elementSelected') {
return
}
const htmlEntitiesRegex = /[\u00A0-\u9999<>\&]/g
const nodeToInsert = htmlEntitiesRegex.test(searchQuery)
? t.jsxExpressionContainer(t.stringLiteral(searchQuery))
: t.jsxText(searchQuery)
insertChild(selectionState.selectedNode as Element, nodeToInsert)
},
},
insertTextBefore: {
showIf: selectionState.type === 'elementSelected' && searchQuery !== '',
section: sections.insertText,
name: `Insert before: ${searchQuery}`,
shortcut: [],
perform: () => {
if (selectionState.type !== 'elementSelected') {
return
}
const htmlEntitiesRegex = /[\u00A0-\u9999<>\&]/g
const nodeToInsert = htmlEntitiesRegex.test(searchQuery)
? t.jsxExpressionContainer(t.stringLiteral(searchQuery))
: t.jsxText(searchQuery)
insertBeforeNode(selectionState.selectedNode as Element, nodeToInsert)
},
},
insertTextAfter: {
showIf: selectionState.type === 'elementSelected' && searchQuery !== '',
section: sections.insertText,
name: `Insert after: ${searchQuery}`,
shortcut: [],
perform: () => {
if (selectionState.type !== 'elementSelected') {
return
}
const htmlEntitiesRegex = /[\u00A0-\u9999<>\&]/g
const nodeToInsert = htmlEntitiesRegex.test(searchQuery)
? t.jsxExpressionContainer(t.stringLiteral(searchQuery))
: t.jsxText(searchQuery)
insertAfterNode(selectionState.selectedNode as Element, nodeToInsert)
},
},
undo: {
showIf: true,
section: sections.general,
name: 'Undo',
shortcut: ['$mod+KeyZ'],
perform: () => selectionState.type === 'elementSelected' && tryToUndoLatestChange(),
},
}
useRegisterActions(
Object.entries(actions)
.filter(([, action]) => action.showIf !== false)
.map(([key, action]) => ({
...action,
id: key,
})),
[actions],
)
return null
}
function CommandBarResults() {
const { results } = useMatches()
return (
typeof item === 'string' ? (
{item}
) : (
{item.name}
{item.shortcut &&
item.shortcut.length > 0 &&
item.shortcut.map((key, idxItem) => {
const isMac = navigator.platform.toUpperCase().startsWith('MAC')
const keyElements = key.split('+')
const symbolReplaceMap: { [key: string]: string } = {
$mod: isMac ? '⌘' : '⌃',
Alt: '⌥',
Shift: '⇧',
Ctrl: '⌃',
}
return keyElements.map((keyElement, idx) => (
{keyElement
.replace('Key', '')
.replace('Shift', '⇧')
.replace('$mod', isMac ? '⌘' : 'Ctrl')}
))
})}
)
}
/>
)
}
function SelectionBox(props: { selectedElement: Node }) {
const { selectedElement } = props
const absolutePosition = elementGetAbsolutePosition(selectedElement)
return (
)
}
function SelectionBoxSibling(props: { selectedElement: Node }) {
const { selectedElement } = props
const absolutePosition = elementGetAbsolutePosition(selectedElement)
return (
)
}
function SelectionBoxChild(props: { selectedNode: Node }) {
const { selectedNode: selectedElement } = props
const absolutePosition = elementGetAbsolutePosition(selectedElement)
const adjustedPosition = {
top: absolutePosition.top + 1,
left: absolutePosition.left + 1,
width: absolutePosition.width - 2,
height: absolutePosition.height - 2,
}
return (
)
}
function makeJumpToCodeLink({ fileName, lineNumber, columnNumber }: FiberSource, schema: string) {
const fileNameNormalized = normalizePath(fileName)
if (schema === 'webstorm') {
return `${schema}://open?file=${fileNameNormalized}&line=${lineNumber}&column=${columnNumber}`
}
return `${schema}://file${fileNameNormalized}:${lineNumber}:${columnNumber}`
}
const htmlTags = [
'a',
'abbr',
'address',
'area',
'article',
'aside',
'audio',
'b',
'base',
'bdi',
'bdo',
'blockquote',
'body',
'br',
'button',
'canvas',
'caption',
'cite',
'code',
'col',
'colgroup',
'data',
'datalist',
'dd',
'del',
'details',
'dfn',
'dialog',
'dir',
'div',
'dl',
'dt',
'em',
'embed',
'fieldset',
'figcaption',
'figure',
'font',
'footer',
'form',
'frame',
'frameset',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'head',
'header',
'hgroup',
'hr',
'html',
'i',
'iframe',
'img',
'input',
'ins',
'kbd',
'label',
'legend',
'li',
'link',
'main',
'map',
'mark',
'marquee',
'menu',
'meta',
'meter',
'nav',
'noscript',
'object',
'ol',
'optgroup',
'option',
'output',
'p',
'param',
'picture',
'pre',
'progress',
'q',
'rp',
'rt',
'ruby',
's',
'samp',
'script',
'section',
'select',
'slot',
'small',
'source',
'span',
'strong',
'style',
'sub',
'summary',
'sup',
'table',
'tbody',
'td',
'template',
'textarea',
'tfoot',
'th',
'thead',
'time',
'title',
'tr',
'track',
'u',
'ul',
'var',
'video',
'wbr',
]