Improve Loading in figma plugin (#866)

* Add skeleton

* improve icon loading
This commit is contained in:
Eric Fennis
2022-11-02 21:01:34 +01:00
committed by GitHub
parent 32c339cabd
commit b40edf1f3c
7 changed files with 85 additions and 18 deletions
@@ -66,3 +66,24 @@ export const getIcons = () => new Promise<LucideIcons>(async (resolve, reject)=>
}
}
});
type EventCallback = (lucideIcons: LucideIcons) => void
export const iconFetchListener = (callback: EventCallback) => {
fetchIcons()
const handleEvent = (event: MessageEvent) => {
if (event.type === 'message' && event?.data?.pluginMessage.type === 'cachedIcons') {
const lucideIcons = event?.data?.pluginMessage?.cachedIcons
callback(lucideIcons)
}
}
window.addEventListener('message', handleEvent)
const removeListener = () => {
window.removeEventListener('message', handleEvent)
}
return removeListener
}
@@ -6,9 +6,11 @@
border-radius: 2px;
appearance: none;
outline: 0;
&:hover {
background: rgba(0, 0, 0, 0.06);
}
&:focus,
&:active {
box-shadow: inset 0 0 0 2px var(--color-blue);
@@ -6,9 +6,10 @@ interface SearchInputProps extends React.HTMLProps<HTMLDivElement> {
value: string,
iconCount: number,
onChange: (event: ChangeEvent<HTMLInputElement>) => void
placeholder: string
}
function SearchInput({ value, onChange, iconCount, className, ...props }: SearchInputProps) {
function SearchInput({ value, onChange, placeholder, className, ...props }: SearchInputProps) {
return (
<div
className="search-input"
@@ -20,7 +21,7 @@ function SearchInput({ value, onChange, iconCount, className, ...props }: Search
type="search"
value={value}
onChange={onChange}
placeholder={`Search ${iconCount} icons`}
placeholder={placeholder}
className="input__field"
/>
</div>
@@ -0,0 +1,25 @@
@keyframes load {
to {
background-position: 200% 0;
}
}
.skeleton {
--block: rgba(0, 0, 0, 0.06);
--loader: hsl(0 0% 89%);
background: linear-gradient(
-75deg,
transparent 40%,
var(--loader),
transparent 60%
) 0 0 / 200% 100%,
var(--block);
border-radius: calc(var(--padding) * 0.5);
animation: load 2s infinite linear;
background-attachment: fixed;
width: 40px;
height: 40px;
border-radius: 2px;
}
@@ -0,0 +1,13 @@
import './Skeleton.scss'
const Skeleton = () => {
return (
<>
{Array.from({length: 48 }, () => (
<div className="skeleton"/>
))}
</>
)
}
export default Skeleton
@@ -6,7 +6,7 @@ type Views = typeof views
import useSearch, { Icon } from '../hooks/useSearch'
import { getIcons } from '../api/fetchIcons'
import { getIcons, iconFetchListener, LucideIcons } from '../api/fetchIcons'
import './interface.scss'
import Menu from '../components/Menu'
@@ -19,21 +19,19 @@ function App() {
const searchResults = useMemo(() => useSearch(icons, tags, query), [icons, query])
const getLatestIcons = async () => {
const lucideIcons = await getIcons()
const handleFetchResponse = async (lucideIcons: LucideIcons) => {
const icons = Object.entries(lucideIcons.iconNodes)
setIcons(Object.entries(lucideIcons.iconNodes))
setIcons(icons)
setTags(lucideIcons.tags)
setVersion(lucideIcons.version)
}
useEffect(() => {
getLatestIcons()
}, [])
const removeListener = iconFetchListener(handleFetchResponse)
if(!icons.length) {
return null
}
return removeListener
}, [])
const View = views?.[page as keyof Views] ?? views.icons
+14 -7
View File
@@ -2,6 +2,7 @@ import IconButton from '../components/IconButton'
import SearchInput from '../components/SearchInput'
import createIconComponent from '../helpers/createIconComponent'
import { Icon } from '../hooks/useSearch'
import Skeleton from '../components/Skeleton/Skeleton'
interface PageProps {
query: string
@@ -24,16 +25,22 @@ const Icons = ({
value={query}
iconCount={icons.length}
onChange={(event) => setQuery(event.target.value)}
placeholder={icons.length ? `Search ${icons.length} icons`: 'Loading icons ..'}
/>
<main>
<div className='icon-grid'>
{searchResults.map(([name, iconNode] :any) => (
<IconButton
name={name}
key={name}
component={createIconComponent(name, iconNode)}
/>
))}
{icons.length ? (
searchResults.map(([name, iconNode]: any) => (
<IconButton
name={name}
key={name}
component={createIconComponent(name, iconNode)}
/>
))
) : (
<Skeleton />
)}
</div>
<footer>
<a