Compare commits
No commits in common. "b85089604f581d9a6d1948f0570d0bf1bc9b6f96" and "fc2dbbd14db03ed718bcd526bd08fadf7a66a938" have entirely different histories.
b85089604f
...
fc2dbbd14d
@ -29,32 +29,30 @@ import EqualIcon from '../../Icons/EqualIcon'
|
|||||||
const operandIconStyle = { fontSize: 8 }
|
const operandIconStyle = { fontSize: 8 }
|
||||||
|
|
||||||
// Longer symbols first so ".." / "<>" / ">=" match before "." / "<" / ">".
|
// Longer symbols first so ".." / "<>" / ">=" match before "." / "<" / ">".
|
||||||
// Wildcards (* ?) are highlighted in place; @ stays as plain text — none are operands.
|
// Wildcards (* ?) and @ stay as plain text — they are not operands.
|
||||||
const WILDCARDS = new Set(['*', '?'])
|
|
||||||
|
|
||||||
const OPERANDS = [
|
const OPERANDS = [
|
||||||
{
|
{
|
||||||
symbol: '..',
|
symbol: '..',
|
||||||
label: <ArrowLeftIcon style={operandIconStyle} />,
|
label: <ArrowLeftIcon style={operandIconStyle} />,
|
||||||
color: 'purple'
|
color: 'cyan'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '<>',
|
symbol: '<>',
|
||||||
label: <NotEqualIcon style={operandIconStyle} />,
|
label: <NotEqualIcon style={operandIconStyle} />,
|
||||||
color: 'magenta'
|
color: 'volcano'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '>=',
|
symbol: '>=',
|
||||||
label: <GreaterThanOrEqualToIcon style={operandIconStyle} />,
|
label: <GreaterThanOrEqualToIcon style={operandIconStyle} />,
|
||||||
color: 'volcano'
|
color: 'orange'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
symbol: '<=',
|
symbol: '<=',
|
||||||
label: <LessThanOrEqualToIcon style={operandIconStyle} />,
|
label: <LessThanOrEqualToIcon style={operandIconStyle} />,
|
||||||
color: 'volcano'
|
color: 'orange'
|
||||||
},
|
},
|
||||||
{ symbol: '|', label: 'OR', color: 'cyan' },
|
{ symbol: '|', label: 'OR', color: 'purple' },
|
||||||
{ symbol: '&', label: 'AND', color: 'cyan' },
|
{ symbol: '&', label: 'AND', color: 'purple' },
|
||||||
{
|
{
|
||||||
symbol: '>',
|
symbol: '>',
|
||||||
label: (
|
label: (
|
||||||
@ -70,7 +68,7 @@ const OPERANDS = [
|
|||||||
{
|
{
|
||||||
symbol: '=',
|
symbol: '=',
|
||||||
label: <EqualIcon style={operandIconStyle} />,
|
label: <EqualIcon style={operandIconStyle} />,
|
||||||
color: 'green'
|
color: 'blue'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -95,13 +93,6 @@ const tokenize = (raw = '') => {
|
|||||||
}
|
}
|
||||||
tokens.push({ type: 'operand', value: match.symbol })
|
tokens.push({ type: 'operand', value: match.symbol })
|
||||||
i += match.symbol.length
|
i += match.symbol.length
|
||||||
} else if (WILDCARDS.has(value[i])) {
|
|
||||||
if (text) {
|
|
||||||
tokens.push({ type: 'text', value: text })
|
|
||||||
text = ''
|
|
||||||
}
|
|
||||||
tokens.push({ type: 'wildcard', value: value[i] })
|
|
||||||
i += 1
|
|
||||||
} else {
|
} else {
|
||||||
text += value[i]
|
text += value[i]
|
||||||
i += 1
|
i += 1
|
||||||
@ -112,14 +103,6 @@ const tokenize = (raw = '') => {
|
|||||||
return tokens
|
return tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderWildcardNode = (char) => {
|
|
||||||
const span = document.createElement('span')
|
|
||||||
span.setAttribute('data-wildcard', char)
|
|
||||||
span.style.color = 'var(--color-primary)'
|
|
||||||
span.textContent = char
|
|
||||||
return span
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalize = (raw = '') =>
|
const normalize = (raw = '') =>
|
||||||
tokenize(raw)
|
tokenize(raw)
|
||||||
.map((token) => token.value)
|
.map((token) => token.value)
|
||||||
@ -319,40 +302,31 @@ const setCaretOffset = (root, targetOffset) => {
|
|||||||
scrollSelectionIntoView(root)
|
scrollSelectionIntoView(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
const walk = (container) => {
|
for (let i = 0; i < root.childNodes.length; i += 1) {
|
||||||
for (let i = 0; i < container.childNodes.length; i += 1) {
|
const child = root.childNodes[i]
|
||||||
const child = container.childNodes[i]
|
const length = nodeLength(child)
|
||||||
const length = nodeLength(child)
|
|
||||||
|
|
||||||
if (child.nodeType === Node.TEXT_NODE) {
|
if (child.nodeType === Node.TEXT_NODE) {
|
||||||
if (remaining <= length) {
|
if (remaining <= length) {
|
||||||
place(child, remaining)
|
place(child, remaining)
|
||||||
return true
|
return
|
||||||
}
|
|
||||||
remaining -= length
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
remaining -= length
|
||||||
if (child.getAttribute?.('data-operand') != null) {
|
continue
|
||||||
if (remaining === 0) {
|
|
||||||
place(container, i)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (remaining <= length) {
|
|
||||||
place(container, i + 1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
remaining -= length
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Element wrappers (e.g. wildcard spans): recurse into children.
|
|
||||||
if (walk(child)) return true
|
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (walk(root)) return
|
if (child.getAttribute?.('data-operand') != null) {
|
||||||
|
if (remaining === 0) {
|
||||||
|
place(root, i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (remaining <= length) {
|
||||||
|
place(root, i + 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remaining -= length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const range = document.createRange()
|
const range = document.createRange()
|
||||||
range.selectNodeContents(root)
|
range.selectNodeContents(root)
|
||||||
@ -367,10 +341,7 @@ const domMatchesTokens = (root, raw) => {
|
|||||||
const tokens = tokenize(raw)
|
const tokens = tokenize(raw)
|
||||||
const children = Array.from(root.childNodes).filter((node) => {
|
const children = Array.from(root.childNodes).filter((node) => {
|
||||||
if (node.nodeType === Node.TEXT_NODE) return (node.textContent ?? '') !== ''
|
if (node.nodeType === Node.TEXT_NODE) return (node.textContent ?? '') !== ''
|
||||||
return (
|
return node.getAttribute?.('data-operand') != null
|
||||||
node.getAttribute?.('data-operand') != null ||
|
|
||||||
node.getAttribute?.('data-wildcard') != null
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Empty editor may keep a single empty text node.
|
// Empty editor may keep a single empty text node.
|
||||||
@ -392,12 +363,6 @@ const domMatchesTokens = (root, raw) => {
|
|||||||
child.nodeType === Node.TEXT_NODE && child.textContent === token.value
|
child.nodeType === Node.TEXT_NODE && child.textContent === token.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (token.type === 'wildcard') {
|
|
||||||
return (
|
|
||||||
child.getAttribute?.('data-wildcard') === token.value &&
|
|
||||||
(child.textContent ?? '') === token.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return child.getAttribute?.('data-operand') === token.value
|
return child.getAttribute?.('data-operand') === token.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -604,8 +569,6 @@ const FilterInput = ({
|
|||||||
for (const item of tokens) {
|
for (const item of tokens) {
|
||||||
if (item.type === 'text') {
|
if (item.type === 'text') {
|
||||||
nextChildren.push(document.createTextNode(item.value))
|
nextChildren.push(document.createTextNode(item.value))
|
||||||
} else if (item.type === 'wildcard') {
|
|
||||||
nextChildren.push(renderWildcardNode(item.value))
|
|
||||||
} else {
|
} else {
|
||||||
nextChildren.push(takeChip(item.value))
|
nextChildren.push(takeChip(item.value))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user