-
Notifications
You must be signed in to change notification settings - Fork 322
fix(icon): [icon] The icon search algorithm is optimized. #2569
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughThe changes in this pull request involve modifications to two Vue components, Changes
Possibly related PRs
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Outside diff range and nitpick comments (3)
examples/sites/demos/pc/app/icon/list-composition-api.vue (1)
Line range hint
1-24
: Consider architectural improvements for better performanceFor better performance with large icon sets, consider these architectural improvements:
- Implement virtual scrolling to handle large icon sets efficiently
- Add debouncing to the search input to prevent excessive re-renders
- Consider implementing a worker for search operations if the icon set is large
Example implementation for debouncing:
import { ref, watch } from 'vue' import { debounce } from 'lodash-es' const searchName = ref('') const debouncedSearch = ref('') watch(searchName, debounce((value) => { debouncedSearch.value = value }, 300))examples/sites/demos/pc/app/icon/list.vue (2)
50-50
: Add input validation for searchConsider adding validation to prevent potential performance issues with complex search patterns.
Add a watcher to sanitize the search input:
+ watch: { + searchName: { + handler(val) { + // Trim whitespace and limit length + this.searchName = val.trim().slice(0, 50); + } + } + }
Line range hint
54-60
: Consider extracting clipboard functionalityThe clipboard interaction could be moved to a separate composable function for better reusability and testing.
Consider creating a composable:
// useClipboard.js import { TinyModal } from '@opentiny/vue' export function useClipboard() { const copyToClipboard = async (text, message) => { try { await navigator.clipboard.writeText(text) TinyModal.message({ message, status: 'info' }) } catch (error) { TinyModal.message({ message: 'Failed to copy', status: 'error' }) } } return { copyToClipboard } }Then use it in the component:
+ import { useClipboard } from './useClipboard' export default { setup() { + const { copyToClipboard } = useClipboard() return { + copyToClipboard } }, methods: { click(name) { - window.navigator.clipboard.writeText(name) - TinyModal.message({ - message: `成功复制图标名称 ${name} `, - status: 'info' - }) + this.copyToClipboard(name, `成功复制图标名称 ${name}`) } } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
examples/sites/demos/pc/app/icon/list-composition-api.vue
(3 hunks)examples/sites/demos/pc/app/icon/list.vue
(3 hunks)
🔇 Additional comments (2)
examples/sites/demos/pc/app/icon/list-composition-api.vue (1)
28-28
: Verify the iconGroups.js module exports
The component now depends on iconGroups.js
. Let's ensure this dependency is properly maintained.
✅ Verification successful
The iconGroups module is properly maintained and exports correctly
The verification shows that iconGroups.js
exists and exports a well-structured object containing icon groupings. The file exports a constant iconGroups
with comprehensive categorization of icons. The search for iconGroupsMap
returned no results, indicating this is likely a new implementation replacing an older pattern.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if iconGroups.js exists and verify its exports
fd -t f "iconGroups.js" -x cat {} \;
# Check for other components that might be affected by this change
rg "iconGroupsMap"
Length of output: 10075
examples/sites/demos/pc/app/icon/list.vue (1)
88-90
: Check browser compatibility for :has() selector
The :has()
selector is a relatively new CSS feature that might not be supported in all browsers. Consider adding a fallback.
Let's check the current browser support:
Consider adding a JavaScript fallback:
+ computed: {
+ shouldShowGroup() {
+ return (groupName) => {
+ return this.filteredIconGroups[groupName]?.length > 0;
+ };
+ }
+ }
- <div class="group-name">
+ <div class="group-name" :style="{ display: shouldShowGroup(groupName) ? 'block' : 'none' }">
✅ Verification successful
Let me gather more information about the browser support requirements of the project.
Based on the findings, I can now generate the final response:
:has()
selector usage requires browser compatibility consideration
The :has()
selector is used in multiple files across the project, including the icon list components. While this selector provides an elegant solution, there are important considerations:
- The project's browserslist configuration ("> 1%, last 2 versions") suggests modern browser support is expected
- However,
:has()
is a relatively recent CSS feature that may affect users with slightly older browsers - The selector is used in core theme components, indicating it's an accepted pattern in the project
Since the project already embraces this modern CSS feature in multiple locations including theme files, and targets modern browsers, the usage is aligned with the project's browser compatibility requirements.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for :has() selector usage and browser compatibility info
rg -l "has\(\s*\+" | while read -r file; do
echo "Found :has() usage in: $file"
done
echo "Browser compatibility for :has() selector:"
curl -s "https://caniuse.com/api/features" | jq '.data."css-has".usage_perc_y'
Length of output: 873
Script:
#!/bin/bash
# Check for browser support configuration or documentation
echo "Checking package.json for browserslist:"
fd package.json --exec cat {} | jq -r '.browserslist'
echo -e "\nChecking .browserslistrc:"
fd .browserslistrc --exec cat {}
echo -e "\nChecking README or documentation for browser support:"
rg -i "browser.*(support|compat)" README.md docs/
Length of output: 2109
<div v-for="(nameList, groupName) in iconGroups" :key="groupName"> | ||
<div class="group-name"> | ||
{{ groupName }} | ||
</div> | ||
<template v-for="name in nameList" :key="name"> | ||
<div class="svgs-item" @click="click(name)"> | ||
<template v-for="name in nameList"> | ||
<div | ||
v-if="searchName === '' || name.toLowerCase().includes(searchName.toLowerCase())" | ||
:key="name" | ||
class="svgs-item" | ||
@click="click(name)" | ||
> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider moving filtering logic to computed property for better performance
The current implementation moves filtering logic directly into the template with v-if
. While this works, it could impact performance for large icon sets as it causes frequent DOM updates.
Consider refactoring to use a computed property:
- <template v-for="name in nameList">
- <div
- v-if="searchName === '' || name.toLowerCase().includes(searchName.toLowerCase())"
- :key="name"
- class="svgs-item"
- @click="click(name)"
- >
+ <template v-for="name in filteredNames(nameList)" :key="name">
+ <div
+ class="svgs-item"
+ @click="click(name)"
+ >
Add to script section:
const filteredNames = (nameList) => {
return computed(() =>
nameList.filter(name =>
searchName.value === '' ||
name.toLowerCase().includes(searchName.value.toLowerCase())
)
)
}
.group-name:not(:has(+ .svgs-item)) { | ||
display: none; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check browser compatibility for :has() selector
The CSS :has()
selector is a relatively new feature and might not be supported in all browsers. Consider adding a fallback solution.
Consider adding a JavaScript-based fallback:
+ const hideEmptyGroups = () => {
+ const groupNames = document.querySelectorAll('.group-name')
+ groupNames.forEach(group => {
+ const hasItems = group.nextElementSibling?.classList.contains('svgs-item')
+ group.style.display = hasItems ? 'block' : 'none'
+ })
+ }
+ onMounted(() => {
+ if (!CSS.supports('selector(:has(*))')) {
+ hideEmptyGroups()
+ watch(searchName, hideEmptyGroups)
+ }
+ })
Committable suggestion skipped: line range outside the PR's diff.
<div v-for="(nameList, groupName) in iconGroups" :key="groupName"> | ||
<div class="group-name"> | ||
{{ groupName }} | ||
</div> | ||
<template v-for="name in nameList" :key="name"> | ||
<div class="svgs-item" @click="click(name)"> | ||
<template v-for="name in nameList"> | ||
<div | ||
v-if="searchName === '' || name.toLowerCase().includes(searchName.toLowerCase())" | ||
:key="name" | ||
class="svgs-item" | ||
@click="click(name)" | ||
> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider optimizing the search performance
The current implementation has potential performance implications:
- Using
v-if
with search filtering inside the template means Vue needs to process each item on every render - Nested v-for loops with inline filtering can be expensive for large icon sets
Consider moving the filtering logic to a computed property:
- <div v-for="(nameList, groupName) in iconGroups" :key="groupName">
- <template v-for="name in nameList">
- <div v-if="searchName === '' || name.toLowerCase().includes(searchName.toLowerCase())"
+ <div v-for="(nameList, groupName) in filteredIconGroups" :key="groupName">
+ <template v-for="name in nameList">
+ <div
// Add in script section:
computed: {
filteredIconGroups() {
if (!this.searchName) return this.iconGroups;
const result = {};
Object.entries(this.iconGroups).forEach(([groupName, names]) => {
const filtered = names.filter(name =>
name.toLowerCase().includes(this.searchName.toLowerCase())
);
if (filtered.length) result[groupName] = filtered;
});
return result;
}
}
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (1)
examples/sites/demos/pc/app/icon/list-composition-api.vue (1)
:has
selector usage needs fallback for broader browser supportThe project's browserslist configuration ("> 1%, last 2 versions") and documentation indicate support for mainstream browsers, but the CSS
:has
selector still has limited support in some browsers that fall within these requirements. Since no polyfills or JavaScript fallbacks were found in the codebase, you should:
- Add a JavaScript fallback for the group visibility logic to ensure compatibility with browsers that don't support
:has
- Consider using a class-based approach with JavaScript to toggle visibility instead of relying solely on
:has
- Example approach:
// Add class to parent when child becomes visible function updateGroupVisibility(group) { const hasVisibleChild = group.querySelector('.svg-visible') group.classList.toggle('has-visible-child', hasVisibleChild) }.svgs-box.has-visible-child .group-name { display: block; }🔗 Analysis chain
Line range hint
79-123
: Verify browser support requirementsLet's check the project's browser support requirements to ensure the CSS approach aligns with them.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check package.json for browserslist config if [ -f package.json ]; then echo "Checking browserslist configuration..." cat package.json | jq '.browserslist' fi # Check for separate browserslist config if [ -f .browserslistrc ]; then echo "Checking .browserslistrc configuration..." cat .browserslistrc fiLength of output: 213
Script:
#!/bin/bash # Check for any polyfills or fallbacks for :has selector rg -i "has.*polyfill|feature.*detect.*has|modernizr.*has" -g "!*.{lock,min.js}" # Check for any browser compatibility comments or documentation fd . -e md -e txt -X rg -i "browser.*support|compatibility" # Look for any JavaScript-based visibility toggle implementations that might serve as fallbacks ast-grep --pattern 'function $FUNC($$$) { $$$ display$$$ $$$ }'Length of output: 444
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
examples/sites/demos/pc/app/icon/list-composition-api.vue
(5 hunks)examples/sites/demos/pc/app/icon/list.vue
(5 hunks)
🔇 Additional comments (7)
examples/sites/demos/pc/app/icon/list-composition-api.vue (3)
29-29
: LGTM! Clean implementation using Vue Composition API
The import and usage of ref follows Vue Composition API best practices.
12-18
: 🛠️ Refactor suggestion
Consider using computed property for filtering icons
The current implementation uses class bindings for filtering, which keeps all DOM elements in memory. For large icon sets, this could impact performance.
Consider using a computed property instead:
- <template v-for="name in nameList" :key="name">
- <div
- :class="{
- 'svg-visible': searchName === '' || name.toLowerCase().includes(searchName.toLowerCase()),
- 'svgs-item': true
- }"
- @click="click(name)"
- >
+ <template v-for="name in filteredNames(nameList)" :key="name">
+ <div class="svgs-item" @click="click(name)">
Add to script section:
const filteredNames = (nameList) => {
return computed(() =>
nameList.filter(name =>
searchName.value === '' ||
name.toLowerCase().includes(searchName.value.toLowerCase())
)
)
}
117-119
:
Ensure browser compatibility for :has() selector
The :has() selector has limited browser support. While it's an elegant solution, a fallback is needed.
Consider adding a JavaScript-based fallback:
const hideEmptyGroups = () => {
const groupNames = document.querySelectorAll('.group-name')
groupNames.forEach(group => {
const hasItems = group.nextElementSibling?.classList.contains('svg-visible')
group.style.display = hasItems ? 'block' : 'none'
})
}
onMounted(() => {
if (!CSS.supports('selector(:has(*))')) {
hideEmptyGroups()
watch(searchName, hideEmptyGroups)
}
})
examples/sites/demos/pc/app/icon/list.vue (4)
51-51
: LGTM!
The searchName initialization is clean and properly placed in the data section.
132-134
: LGTM!
The CSS visibility control using classes is well-organized and follows good practices.
7-18
:
Performance optimization needed for icon filtering
The current implementation still performs filtering in the template, which can impact performance with large icon sets. This is particularly concerning for the following reasons:
- The toLowerCase() comparisons run on every render
- The filtering logic is executed for every item in the template
Consider moving the filtering to a computed property:
- <div v-for="(nameList, groupName) in iconGroups" :key="groupName" class="svgs-box">
+ <div v-for="(nameList, groupName) in filteredIcons" :key="groupName" class="svgs-box">
<div class="group-name">
{{ groupName }}
</div>
<template v-for="name in nameList" :key="name">
<div
- :class="{
- 'svg-visible': searchName === '' || name.toLowerCase().includes(searchName.toLowerCase()),
- 'svgs-item': true
- }"
+ class="svgs-item svg-visible"
@click="click(name)"
>
Add in script section:
computed: {
filteredIcons() {
if (!this.searchName) return this.iconGroups;
const searchLower = this.searchName.toLowerCase();
return Object.entries(this.iconGroups).reduce((acc, [groupName, names]) => {
const filtered = names.filter(name =>
name.toLowerCase().includes(searchLower)
);
if (filtered.length) acc[groupName] = filtered;
return acc;
}, {});
}
}
128-130
: Verify browser compatibility for :has() selector
The :has()
selector is a relatively new CSS feature that might not be supported in all browsers. This could affect the visibility of group names in older browsers.
Consider adding a fallback for browsers that don't support :has():
.svgs-box:has(> .svg-visible) .group-name {
display: block;
}
+/* Fallback for browsers without :has() support */
+@supports not (selector(:has(*))) {
+ .svgs-box .group-name {
+ display: block;
+ }
+}
✅ Verification successful
Based on the search results, I found that the project explicitly states browser compatibility requirements in examples/sites/demos/pc/webdoc/envpreparation-open-en.md
:
Browser compatibility
TinyVue
The latest versions of mainstream browsers are supported: Chrome, Edge, Firefox, Opera, and Safari. No longer supportedInternet Explorer
.
Since the :has()
selector is supported in all these modern browsers (Chrome 105+, Edge 105+, Firefox 103+, Opera 91+, and Safari 15.4+) and the project explicitly targets only modern browsers, the usage of :has()
selector is appropriate and doesn't require a fallback.
:has()
selector usage is compatible with project's browser support requirements
The project explicitly targets modern browsers that all support the :has()
selector, making the current implementation appropriate without requiring fallbacks.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for browser compatibility comments in the codebase
rg -i "browser.?compat|support" --type md
Length of output: 10332
PR
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information
Summary by CodeRabbit
New Features
Bug Fixes
Refactor