diff --git a/assets/stylesheets/App.css b/assets/stylesheets/App.css index 4c0d0bfc..542ed12d 100644 --- a/assets/stylesheets/App.css +++ b/assets/stylesheets/App.css @@ -2358,3 +2358,189 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton { .ant-table-wrapper tfoot > tr > td { padding: 12px 8px; } + +/* FilterInput */ +.filter-input-operand-icon { + font-size: 8px; +} + +.filter-input-operand-icon--chevron { + font-size: 11px; +} + +.filter-input-operand-icon--gt { + margin-left: 1px; +} + +.filter-input-operand-icon--lt { + margin-right: 1px; +} + +.filter-input-wildcard, +[data-wildcard] { + color: var(--color-primary); +} + +.filter-input-operand-tag { + margin-inline-end: 0; + margin-right: 0; + height: 18px; + cursor: default; + padding: 0 4px; + margin-top: -2px; + line-height: 1.37; + user-select: none; + pointer-events: none; + flex-shrink: 0; + font-weight: 700; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.filter-input-operand-chip { + display: inline-flex; + align-items: center; + vertical-align: middle; + margin: 0 2px; + user-select: none; + flex-shrink: 0; +} + +.filter-input-property-filter { + display: flex; + flex-direction: column; + max-width: 280px; + margin: 0; + vertical-align: top; + overflow: hidden; + --property-filter-panel-max-height: 220px; +} + +.filter-input-property-filter-quick { + flex-shrink: 0; +} + +.filter-input-property-filter-divider { + margin: 4px 0; + flex-shrink: 0; +} + +.filter-input-property-filter + > .filter-input-property-filter-scroll.scrollbox-inner { + height: auto !important; + max-height: var(--property-filter-panel-max-height); + max-width: 280px; + flex-shrink: 1; + min-height: 0; +} + +.filter-input-property-filter + > .filter-input-property-filter-scroll.scrollbox-inner + .simplebar-wrapper { + max-height: var(--property-filter-panel-max-height); +} + +.filter-input-property-filter .simplebar-content-wrapper { + max-width: none; +} + +.filter-input-property-filter .simplebar-content { + display: inline-block; +} + +.filter-input-property-filter-body { + padding: 12px 26px 12px 16px; + max-width: 280px; + box-sizing: border-box; +} + +.filter-input-property-filter-content { + width: max-content; + max-width: 100%; +} + +.filter-input-affix { + width: 100%; + display: inline-flex; + position: relative; +} + +.filter-input-affix:not(.filter-input-affix--disabled) { + cursor: text; +} + +.filter-input-affix--disabled { + cursor: not-allowed; +} + +.filter-input-affix--elevated { + z-index: 3 !important; +} + +.filter-input-editor::-webkit-scrollbar { + display: none; + height: 0; +} + +.filter-input-editor { + display: block; + padding: 0; + border: none; + border-radius: 0; + outline: none; + box-shadow: none; + background: transparent; + flex: auto; + min-width: 0; + width: 100%; + font-size: inherit; + line-height: inherit; + color: inherit; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: none; + -ms-overflow-style: none; + word-break: keep-all; + overflow-wrap: normal; + caret-color: var(--ant-color-text, inherit); + cursor: text; +} + +.filter-input-affix--disabled .filter-input-editor { + cursor: not-allowed; +} + +.filter-input-placeholder { + position: absolute; + inset-inline-start: var(--ant-input-padding-inline, 11px); + inset-inline-end: var(--ant-input-padding-inline, 11px); + top: 0; + bottom: 0; + display: flex; + align-items: center; + color: var(--ant-color-text-placeholder, rgba(0, 0, 0, 0.25)); + pointer-events: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + z-index: 1; +} + +.filter-input-affix--has-clear .filter-input-placeholder { + inset-inline-end: calc(var(--ant-input-padding-inline, 11px) + 22px); +} + +.filter-input-popover .ant-popover-inner { + padding: 4px; +} + +.filter-input-popover-anchor { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 0; + pointer-events: none; +} diff --git a/src/components/Dashboard/common/FilterInput.jsx b/src/components/Dashboard/common/FilterInput.jsx index 212770fa..5896ddd2 100644 --- a/src/components/Dashboard/common/FilterInput.jsx +++ b/src/components/Dashboard/common/FilterInput.jsx @@ -6,7 +6,7 @@ import { useRef, useState } from 'react' -import { ConfigProvider, Popover, Tag, theme } from 'antd' +import { ConfigProvider, Popover, Tag } from 'antd' import { CloseCircleFilled } from '@ant-design/icons' import PropTypes from 'prop-types' import { createRoot } from 'react-dom/client' @@ -28,8 +28,6 @@ import NotEqualIcon from '../../Icons/NotEqualIcon' import EqualIcon from '../../Icons/EqualIcon' import { Divider } from 'antd' -const operandIconStyle = { fontSize: 8 } - const PROPERTY_FILTER_PANEL_MAX_HEIGHT = 220 // Longer symbols first so ".." / "<>" / ">=" match before "." / "<" / ">". @@ -39,22 +37,24 @@ const WILDCARDS = new Set(['*', '?']) const OPERANDS = [ { symbol: '..', - label: , + label: ( + + ), color: 'purple' }, { symbol: '<>', - label: , + label: , color: 'magenta' }, { symbol: '>=', - label: , + label: , color: 'volcano' }, { symbol: '<=', - label: , + label: , color: 'volcano' }, { symbol: '|', label: 'OR', color: 'cyan' }, @@ -62,18 +62,20 @@ const OPERANDS = [ { symbol: '>', label: ( - + ), color: 'orange' }, { symbol: '<', - label: , + label: ( + + ), color: 'orange' }, { symbol: '=', - label: , + label: , color: 'green' } ] @@ -119,7 +121,7 @@ const tokenize = (raw = '') => { const renderWildcardNode = (char) => { const span = document.createElement('span') span.setAttribute('data-wildcard', char) - span.style.color = 'var(--color-primary)' + span.className = 'filter-input-wildcard' span.textContent = char return span } @@ -452,7 +454,6 @@ const FilterInput = ({ }) => { const { getPrefixCls, direction } = useContext(ConfigProvider.ConfigContext) const prefixCls = getPrefixCls('input') - const { token } = theme.useToken() const { themeConfig } = useThemeContext() const themeConfigRef = useRef(themeConfig) themeConfigRef.current = themeConfig @@ -479,6 +480,7 @@ const FilterInput = ({ const [internalValue, setInternalValue] = useState(value ?? '') const [quickFiltersHeight, setQuickFiltersHeight] = useState(0) const [quickFilterModalOpen, setQuickFilterModalOpen] = useState(false) + const [hover, setHover] = useState(false) const clearTagRoots = useCallback(() => { tagRootsRef.current.forEach((root) => { @@ -502,22 +504,7 @@ const FilterInput = ({ {operand?.label ?? symbol} @@ -530,12 +517,7 @@ const FilterInput = ({ const wrapper = document.createElement('span') wrapper.setAttribute('data-operand', symbol) wrapper.setAttribute('contenteditable', 'false') - wrapper.style.display = 'inline-flex' - wrapper.style.alignItems = 'center' - wrapper.style.verticalAlign = 'middle' - wrapper.style.margin = '0 2px' - wrapper.style.userSelect = 'none' - wrapper.style.flexShrink = '0' + wrapper.className = 'filter-input-operand-chip' const root = createRoot(wrapper) root.render(renderTagNode(symbol, themeConfigRef.current)) @@ -922,16 +904,10 @@ const FilterInput = ({ }} className='filter-input-property-filter' style={{ - display: 'inline-flex', - flexDirection: 'column', - maxWidth: 280, - maxHeight: quickFiltersHeight + PROPERTY_FILTER_PANEL_MAX_HEIGHT, - margin: -4, - verticalAlign: 'top', - overflow: 'hidden' + '--filter-input-property-filter-max-height': `${quickFiltersHeight + PROPERTY_FILTER_PANEL_MAX_HEIGHT}px` }} > -
+
- +
+ +
-
-
+
+
{ if (!disabled && !quickFilterModalOpenRef.current) { editorRef.current?.focus() } }} + onMouseEnter={() => { + setHover(true) + }} + onMouseLeave={() => { + setHover(false) + }} > - {showPlaceholder && ( - + {placeholder} )} @@ -1182,31 +1114,6 @@ const FilterInput = ({ if (next !== valueRef.current) emitChange(next) onBlur?.(event) }} - style={{ - // Match antd's `> input.ant-input` resets inside affix-wrapper - display: 'block', - padding: 0, - border: 'none', - borderRadius: 0, - outline: 'none', - boxShadow: 'none', - background: 'transparent', - flex: 'auto', - minWidth: 0, - width: '100%', - fontSize: 'inherit', - lineHeight: 'inherit', - color: 'inherit', - whiteSpace: 'nowrap', - overflowX: 'auto', - overflowY: 'hidden', - scrollbarWidth: 'none', - msOverflowStyle: 'none', - wordBreak: 'keep-all', - overflowWrap: 'normal', - caretColor: token.colorText, - cursor: disabled ? 'not-allowed' : 'text' - }} /> {showClear && ( @@ -1231,24 +1138,10 @@ const FilterInput = ({ placement='bottomLeft' arrow={false} trigger={[]} - styles={{ - body: { - padding: 8 - } - }} + classNames={{ root: 'filter-input-popover' }} > {/* Anchor only — keep Popover from wrapping the Compact item (z-index/focus ring). */} - + )}