diff --git a/packages/lucide-figma/manifest.json b/packages/lucide-figma/manifest.json index ba36818..79d07fa 100644 --- a/packages/lucide-figma/manifest.json +++ b/packages/lucide-figma/manifest.json @@ -9,9 +9,18 @@ "worker": "dist/src/worker/worker.html" }, "parameterOnly": false, - "parameters": [{ - "name": "Icon", - "key": "icon-name", - "description": "Enter the name of the icon you want to insert." - }] + "parameters": [ + { + "name": "Icon", + "key": "icon-name", + "description": "Enter the name of the icon you want to insert." + }, + { + "name": "Size", + "key": "size", + "description": "Enter the size of the icon.", + "allowFreeform": true, + "optional": true + } + ] } diff --git a/packages/lucide-figma/src/components/EditBar/EditBar.tsx b/packages/lucide-figma/src/components/EditBar/EditBar.tsx new file mode 100644 index 0000000..f29f0bd --- /dev/null +++ b/packages/lucide-figma/src/components/EditBar/EditBar.tsx @@ -0,0 +1,5 @@ +const EditBar = () => { + +} + +export default EditBar diff --git a/packages/lucide-figma/src/components/EditBar/index.ts b/packages/lucide-figma/src/components/EditBar/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/lucide-figma/src/components/Menu/Menu.scss b/packages/lucide-figma/src/components/Menu/Menu.scss new file mode 100644 index 0000000..b1bb867 --- /dev/null +++ b/packages/lucide-figma/src/components/Menu/Menu.scss @@ -0,0 +1,30 @@ +.menu { + display: flex; + align-items: center; + flex-shrink: 0; + height: 40px; + line-height: 40px; + border-bottom: 1px solid var(--color-border, #e5e5e5); + justify-content: flex-start; +} + +.menu-item { + font-weight: 500; + font-size: 11px; + color: var(--color-text-tertiary, #b3b3b3); + padding: 0 8px; + text-transform: capitalize; + cursor: pointer; + + &.active { + color: var(--color-text, #333333); + } + + &:first-child { + padding-left: 16px; + } + + &:last-child { + padding-right: 16px; + } +} diff --git a/packages/lucide-figma/src/components/Menu/Menu.tsx b/packages/lucide-figma/src/components/Menu/Menu.tsx new file mode 100644 index 0000000..a5ccf32 --- /dev/null +++ b/packages/lucide-figma/src/components/Menu/Menu.tsx @@ -0,0 +1,23 @@ +import { useState } from 'react' +import './Menu.scss' + +interface MenuProps { + page: string + setPage: (page:string) => void +} + +const menuItems = ['icons', 'info'] + +const Menu = ({page, setPage = (page) => {}}: MenuProps) => { + return ( + + { menuItems.map((menuItem) => ( + setPage(menuItem)}> + {menuItem} + + )) } + + ) +} + +export default Menu diff --git a/packages/lucide-figma/src/components/Menu/index.ts b/packages/lucide-figma/src/components/Menu/index.ts new file mode 100644 index 0000000..51d857a --- /dev/null +++ b/packages/lucide-figma/src/components/Menu/index.ts @@ -0,0 +1 @@ +export { default } from './Menu' diff --git a/packages/lucide-figma/src/components/SearchInput/SearchInput.scss b/packages/lucide-figma/src/components/SearchInput/SearchInput.scss index 316c846..e6acd48 100644 --- a/packages/lucide-figma/src/components/SearchInput/SearchInput.scss +++ b/packages/lucide-figma/src/components/SearchInput/SearchInput.scss @@ -1,9 +1,11 @@ .search-input { + position: relative; + display: flex; + align-items: center; + .icon { position: absolute; - top: 0; - left: 0; - padding: 4px; + left: 16px; } input { width: 100%; diff --git a/packages/lucide-figma/src/components/icons/SearchIcon.tsx b/packages/lucide-figma/src/components/icons/SearchIcon.tsx index 8134a92..b33ce94 100644 --- a/packages/lucide-figma/src/components/icons/SearchIcon.tsx +++ b/packages/lucide-figma/src/components/icons/SearchIcon.tsx @@ -1,24 +1,22 @@ import { createElement, forwardRef } from 'react' -const SearchIcon = forwardRef((props: any, ref) => createElement( - 'svg', - { - xmlns: "http://www.w3.org/2000/svg", - width: 32, - height: 32, - clipRule: 'evenodd', - fillRule: 'evenodd', - ref, - ...props, - }, - [ - createElement( - 'path', { - d: 'm20 15c0 2.7614-2.2386 5-5 5s-5-2.2386-5-5 2.2386-5 5-5 5 2.2386 5 5zm-1.1256 4.5815c-1.0453.8849-2.3975 1.4185-3.8744 1.4185-3.3137 0-6-2.6863-6-6s2.6863-6 6-6 6 2.6863 6 6c0 1.4769-.5336 2.8291-1.4185 3.8744l4.2721 4.272-.7072.7072z', - key: 'path' - } - ) - ] - )) + +const SearchIcon = (props: any) => ( + + + +) export default SearchIcon diff --git a/packages/lucide-figma/src/interface/interface.scss b/packages/lucide-figma/src/interface/interface.scss index 79ae610..532583d 100644 --- a/packages/lucide-figma/src/interface/interface.scss +++ b/packages/lucide-figma/src/interface/interface.scss @@ -9,6 +9,17 @@ format('woff'); } +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('https://rsms.me/inter/font-files/Inter-Medium.woff2?v=3.9') + format('woff2'), + url('https://rsms.me/inter/font-files/Inter-Medium.woff?v=3.9') + format('woff'); +} + :root { --color-blue: #18a0fb; --color-black: #333; @@ -47,3 +58,48 @@ footer { color: inherit; } } + +.floating { + position: absolute; + bottom: -10px; + left: 0; + padding: 10px; + background: blue; +} + +.info-page { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 24px; +} + +.lucide-logo { + width: 160px; +} + +.version { + margin-top: 8px; + padding: 8px; + font-weight: bold; + font-size: 18px; + color: rgba(0, 0, 0, 0.5); +} + +.link-list { + width: 160px; + margin: 0 auto; + padding-top: 24px; +} + +.info-link { + color: var(--color-blue); + display: block; + padding: 2px 0; + text-decoration: none; + + &:hover { + text-decoration: underline; + } +} diff --git a/packages/lucide-figma/src/interface/interface.tsx b/packages/lucide-figma/src/interface/interface.tsx index 37cb335..3d3176f 100644 --- a/packages/lucide-figma/src/interface/interface.tsx +++ b/packages/lucide-figma/src/interface/interface.tsx @@ -1,15 +1,17 @@ import { useEffect, useMemo, useState } from 'react' -import { createReactComponent } from 'lucide-react' import ReactDOM from 'react-dom' +import * as views from '../views' + +type Views = typeof views -import IconButton from '../components/IconButton' -import SearchInput from '../components/SearchInput' import useSearch, { Icon } from '../hooks/useSearch' import { getIcons } from '../api/fetchIcons' import './interface.scss' +import Menu from '../components/Menu' function App() { + const [page, setPage] = useState('icons') const [query, setQuery] = useState('') const [icons, setIcons] = useState([]) const [tags, setTags] = useState({}) @@ -33,33 +35,20 @@ function App() { return null } + const View = views?.[page as keyof Views] ?? views.icons + return ( - setQuery(event.target.value)} + + - - - {searchResults.map(([name, iconNode] :any) => ( - - ))} - - - ) } diff --git a/packages/lucide-figma/src/main.ts b/packages/lucide-figma/src/main.ts index b1a7342..596bf35 100644 --- a/packages/lucide-figma/src/main.ts +++ b/packages/lucide-figma/src/main.ts @@ -5,6 +5,12 @@ figma.showUI(__uiFiles__.worker, { visible: false }) let cachedIcons: LucideIcons +type InsertableNodes = FrameNode | GroupNode + +function isInsertableNode (node: SceneNode): node is InsertableNodes { + return ['FRAME', 'GROUP'].includes(node.type) +} + const setResults = ({result, query, lucideIcons} : { result: SuggestionResults, query: string, lucideIcons: LucideIcons }) => { const icons = Object.entries(lucideIcons.iconNodes); @@ -16,33 +22,65 @@ const setResults = ({result, query, lucideIcons} : { result: SuggestionResults, result.setSuggestions(suggestions) } +// const styles = figma.getLocalPaintStyles(); +// const styleNames = styles.map((style) => style.name); +// console.log(styleNames); + figma.parameters.on('input', async ({ parameters, key, query, result }) => { if (key === 'icon-name') { - console.log('typ tpy', query); cachedIcons = await figma.clientStorage.getAsync(`lucide-icons`) - console.log('cachedIcons', cachedIcons); if(cachedIcons && cachedIcons.iconNodes && cachedIcons.tags) { setResults({result, query, lucideIcons: cachedIcons}) } } + if(key === 'size') { + const iconSizes = [24,36,48,72] + result.setSuggestions(iconSizes.map((size)=>({ + name: size.toString(), + data: size + }))) + } }) -const drawIcon = ({icon: {name, svg}}: any) => { +const drawIcon = ({icon: {name, svg, size }}: any) => { const min = 0 const max = 100 const randomPosition = () => Math.floor(Math.random() * (max - min + 1) + min) + const icon = figma.createNodeFromSvg(svg) + icon.setPluginData('isLucideIcon', 'true') + icon.setPluginData('iconName', name) + + const pluginData = icon.getPluginData('isLucideIcon') icon.name = name icon.x = Math.round(figma.viewport.center.x + randomPosition()) icon.y = Math.round(figma.viewport.center.y + randomPosition()) + + if(figma.currentPage.selection.length) { + let currentSelection = figma.currentPage.selection[0] + const isLucideIcon = currentSelection.getPluginData('isLucideIcon') + + // if(isLucideIcon && currentSelection?.parent) { + // return + // // currentSelection = currentSelection.parent as SceneNode + // } + + if(!isLucideIcon && isInsertableNode(currentSelection)) { + icon.x = currentSelection.type === 'GROUP' ? currentSelection.x : 0 + icon.y = currentSelection.type === 'GROUP' ? currentSelection.y : 0 + + currentSelection.appendChild(icon) + } + } + figma.currentPage.selection = [icon] // lock children - icon.children.forEach((vectorNode, key) => { - icon.children[key].locked = true - }); + // icon.children.forEach((vectorNode, key) => { + // icon.children[key].locked = true + // }); } const setCachedIcons = async (pluginMessage: any) => { @@ -66,7 +104,6 @@ const getCachedIcons = async () => { getCachedIcons() figma.ui.onmessage = (event) => { - console.log(event, 'main'); switch (event.type) { case "drawIcon": drawIcon(event) @@ -90,7 +127,12 @@ figma.ui.onmessage = (event) => { figma.on('run', event => { if(event.parameters) { - figma.ui.postMessage({ type: 'getSvg', iconName: event.parameters['icon-name'], cachedIcons }) + figma.ui.postMessage({ + type: 'getSvg', + iconName: event.parameters['icon-name'], + size: event.parameters['size'], + cachedIcons + }) } else { figma.showUI(__uiFiles__.interface, { width: 300, height: 400 }) } diff --git a/packages/lucide-figma/src/views/Icons.tsx b/packages/lucide-figma/src/views/Icons.tsx new file mode 100644 index 0000000..71e1a03 --- /dev/null +++ b/packages/lucide-figma/src/views/Icons.tsx @@ -0,0 +1,53 @@ +import { createReactComponent } from 'lucide-react' + +import IconButton from '../components/IconButton' +import SearchInput from '../components/SearchInput' +import { Icon } from '../hooks/useSearch' + +interface PageProps { + query: string + setQuery: (query:string) => void + searchResults: Icon[] + icons: Icon[] + version: string +} + +const Icons = ({ + query, + setQuery, + searchResults, + icons, + version +}: PageProps) => { + return ( + <> + setQuery(event.target.value)} + /> + + + {searchResults.map(([name, iconNode] :any) => ( + + ))} + + + + > + ) +} + +export default Icons diff --git a/packages/lucide-figma/src/views/Info.tsx b/packages/lucide-figma/src/views/Info.tsx new file mode 100644 index 0000000..2565b80 --- /dev/null +++ b/packages/lucide-figma/src/views/Info.tsx @@ -0,0 +1,64 @@ +import { SyntheticEvent } from "react" + +interface PageProps { + version: string +} + +const Info = ({ version }: PageProps) => { + const menuItems = [ + { + name: 'Report a bug', + url: 'https://github.com/lucide-icons/lucide/issues' + }, + { + name: 'Contribute an icon', + url: 'https://github.com/lucide-icons/lucide/blob/master/CONTRIBUTING.md' + }, + { + name: 'Website', + url: 'https://lucide.dev' + }, + { + name: 'Repository', + url: 'https://github.com/lucide-icons/lucide' + }, + { + name: 'License', + url: 'https://lucide.dev/license' + }, + { + name: 'Community Page', + url: 'https://www.figma.com/community/plugin/939567362549682242/Lucide-Icons' + }, + { + name: 'Supported Frameworks', + url: 'https://lucide.dev/packages' + } + ] + + const onClick = (url: string) => (event: SyntheticEvent) => { + event.preventDefault() + + window.open(url,'_blank') + } + + return ( + + + + v{version} + + + { + menuItems.map(({ name, url }) => ( + + {name} + + )) + } + + + ) +} + +export default Info diff --git a/packages/lucide-figma/src/views/index.ts b/packages/lucide-figma/src/views/index.ts new file mode 100644 index 0000000..f880a75 --- /dev/null +++ b/packages/lucide-figma/src/views/index.ts @@ -0,0 +1,2 @@ +export { default as icons } from './Icons' +export { default as info } from './Info' diff --git a/packages/lucide-figma/src/worker/worker.ts b/packages/lucide-figma/src/worker/worker.ts index 05c9f17..a1297c6 100644 --- a/packages/lucide-figma/src/worker/worker.ts +++ b/packages/lucide-figma/src/worker/worker.ts @@ -14,20 +14,26 @@ const getLatestIcons = async ({ cachedIcons }: any) => { }, "*") } -const getSvg = async ({ cachedIcons, iconName }: { cachedIcons: LucideIcons, iconName: string }) => { +const getSvg = async ({ cachedIcons, iconName, size = 24 }: { cachedIcons: LucideIcons, iconName: string, size: number }) => { if (!cachedIcons) { return; } + console.log( iconName, size) + const iconNode = cachedIcons.iconNodes[iconName]; if (iconNode) { const IconComponent = createReactComponent(iconName, iconNode) - const svg = renderToString(createElement(IconComponent)); + const svg = renderToString(createElement(IconComponent, { size })); parent.postMessage({ pluginMessage: { type: 'drawIcon', - icon: { name, svg } + icon: { + name: iconName, + svg, + size + } }}, '*') parent.postMessage({ pluginMessage: { @@ -56,5 +62,3 @@ window.onmessage = async (event) => { break; } } - -console.log('Hello world!')
+ v{version} +