diff --git a/packages/lucide-figma/src/api/fetchIcons.ts b/packages/lucide-figma/src/api/fetchIcons.ts index ba3bf3fee..15e471562 100644 --- a/packages/lucide-figma/src/api/fetchIcons.ts +++ b/packages/lucide-figma/src/api/fetchIcons.ts @@ -66,3 +66,24 @@ export const getIcons = () => new Promise(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 +} diff --git a/packages/lucide-figma/src/components/IconButton/IconButton.scss b/packages/lucide-figma/src/components/IconButton/IconButton.scss index e70e198f4..78980aa57 100644 --- a/packages/lucide-figma/src/components/IconButton/IconButton.scss +++ b/packages/lucide-figma/src/components/IconButton/IconButton.scss @@ -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); diff --git a/packages/lucide-figma/src/components/SearchInput/SearchInput.tsx b/packages/lucide-figma/src/components/SearchInput/SearchInput.tsx index f74becf25..bd7ddbd4c 100644 --- a/packages/lucide-figma/src/components/SearchInput/SearchInput.tsx +++ b/packages/lucide-figma/src/components/SearchInput/SearchInput.tsx @@ -6,9 +6,10 @@ interface SearchInputProps extends React.HTMLProps { value: string, iconCount: number, onChange: (event: ChangeEvent) => void + placeholder: string } -function SearchInput({ value, onChange, iconCount, className, ...props }: SearchInputProps) { +function SearchInput({ value, onChange, placeholder, className, ...props }: SearchInputProps) { return (
diff --git a/packages/lucide-figma/src/components/Skeleton/Skeleton.scss b/packages/lucide-figma/src/components/Skeleton/Skeleton.scss new file mode 100644 index 000000000..f4fd7a9b7 --- /dev/null +++ b/packages/lucide-figma/src/components/Skeleton/Skeleton.scss @@ -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; +} diff --git a/packages/lucide-figma/src/components/Skeleton/Skeleton.tsx b/packages/lucide-figma/src/components/Skeleton/Skeleton.tsx new file mode 100644 index 000000000..c12930022 --- /dev/null +++ b/packages/lucide-figma/src/components/Skeleton/Skeleton.tsx @@ -0,0 +1,13 @@ +import './Skeleton.scss' + +const Skeleton = () => { + return ( + <> + {Array.from({length: 48 }, () => ( +
+ ))} + + ) +} + +export default Skeleton diff --git a/packages/lucide-figma/src/interface/interface.tsx b/packages/lucide-figma/src/interface/interface.tsx index 3d3176f84..cba8958d4 100644 --- a/packages/lucide-figma/src/interface/interface.tsx +++ b/packages/lucide-figma/src/interface/interface.tsx @@ -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 diff --git a/packages/lucide-figma/src/views/Icons.tsx b/packages/lucide-figma/src/views/Icons.tsx index d8194ef34..9645ebdcd 100644 --- a/packages/lucide-figma/src/views/Icons.tsx +++ b/packages/lucide-figma/src/views/Icons.tsx @@ -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 ..'} />
- {searchResults.map(([name, iconNode] :any) => ( - - ))} + {icons.length ? ( + searchResults.map(([name, iconNode]: any) => ( + + )) + ) : ( + + )} +