Skip to content

Commit

Permalink
Merge pull request #31 from bridgedxyz/designer/icon
Browse files Browse the repository at this point in the history
0.1.4 release - added icons loader, icon detection, icon button detection, illust detection, and more
  • Loading branch information
softmarshmallow authored Nov 17, 2020
2 parents 2991a5b + 8e9137b commit 10fa683
Show file tree
Hide file tree
Showing 20 changed files with 296 additions and 85 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"git.ignoreLimitWarning": true
}
3 changes: 2 additions & 1 deletion figma/src/app/components/box-directory-input.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from "react";
import { ReactBoxSnippetGenerator } from "@bridged.xyz/box";
import Button from "@material-ui/core/Button";

function buildFolderSelector(): HTMLInputElement {
const fileSelector = document.createElement('input');
Expand Down Expand Up @@ -57,7 +58,7 @@ export class BoxDirectoryInput extends React.Component {
render() {
return <>
{/* <input type="file" name="file" multiple /> */}
<button onClick={this.handleFileSelect}>Select src dir</button>
<Button onClick={this.handleFileSelect}>Select src dir</Button>
</>
}
}
10 changes: 5 additions & 5 deletions figma/src/app/components/box-tab.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Button from '@material-ui/core/Button'
import * as React from 'react'
import { EK_GENERATED_CODE_PLAIN, EK_PREVIEW_SOURCE } from '../constants/ek.constant'
import { BoxDirectoryInput } from './box-directory-input'


Expand Down Expand Up @@ -34,12 +34,12 @@ export class BoxTab extends React.Component {
<h6>Box section start</h6>
<p>currently selected component is... { }</p>
<div />
<button onClick={this.onClickOpenStorybook}>
<Button onClick={this.onClickOpenStorybook}>
open in storybook
</button>
<button onClick={this.onClickOpenGithub}>
</Button>
<Button onClick={this.onClickOpenGithub}>
open in github
</button>
</Button>
<BoxDirectoryInput />
<div />
<h6>Box section end</h6>
Expand Down
2 changes: 2 additions & 0 deletions figma/src/app/components/dev-tools.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from "react";
import { IconsLoader } from "./icons-loader";



Expand Down Expand Up @@ -34,6 +35,7 @@ export class DevTools extends React.Component {
<button onClick={this.hideAllOnlyText}>hide only text</button>
<button onClick={this.onClickRandomize}>randomize</button>
<button onClick={this.onClickOpenConsole}>open in console</button>
<div><IconsLoader /></div>
</div>
}
}
8 changes: 4 additions & 4 deletions figma/src/app/components/highlight.css
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@

code{
code {
width: max-content;
height: auto;
}


pre {
margin-left:-16px;
margin-left: -16px;
margin-right: -16px;
padding: 8px;
overflow: scroll;
}

.copy{
.sticky-actions {
position: sticky;
right: 4px;
margin-right: 16px;
top: 4px;
}
7 changes: 4 additions & 3 deletions figma/src/app/components/highlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { EK_COPIED } from '../constants/ek.constant';
import { quickLook } from '../../quicklook';
import { Widget } from '@bridged.xyz/flutter-builder/lib';
import { notify } from '@bridged.xyz/design-sdk/lib/figma';
import Button from '@material-ui/core/Button';
dartLang(Prism);
// endregion

Expand Down Expand Up @@ -57,14 +58,14 @@ export default class Highlight extends React.Component<Props, State> {
setLoadingState(false)
notify(parent, "compile failed. view console for details.", 2)
})

}

render() {
return <code>
<div>
<button className="copy" onClick={this.onCopyClicked}>copy code</button>
<button className="quick look" disabled={this.state.isLaunchingConsole} onClick={this.onQuickLookClicked}>{this.state.isLaunchingConsole ? 'launching..' : 'quick look'}</button>
<div style={{ height: "24px" }} />
<Button variant="outlined" size="medium" className="sticky-actions" onClick={this.onCopyClicked}>copy code</Button>
<Button variant="outlined" size="medium" className="sticky-actions" disabled={this.state.isLaunchingConsole} onClick={this.onQuickLookClicked}>{this.state.isLaunchingConsole ? 'launching..' : 'quick look'}</Button>
</div>
<PrismHighlight {...defaultProps} Prism={Prism} code={this.props.code} language={this.props.language}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
Expand Down
142 changes: 142 additions & 0 deletions figma/src/app/components/icons-loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React, { useEffect, useState } from "react"
import { IconConfig } from "@bridged.xyz/client-sdk/lib"
import Tooltip from "@material-ui/core/Tooltip";
import IconButton from "@material-ui/core/IconButton";
import GridList from "@material-ui/core/GridList";
import GridListTile from "@material-ui/core/GridListTile";
import { EK_CREATE_ICON } from "../constants/ek.constant";
import { loadSvg, makeIconUrl } from "../../assets-repository/icons-generator";
import TextField from "@material-ui/core/TextField";
import LinearProgress from "@material-ui/core/LinearProgress";
const CONFIG_JSON_S3 = "https://reflect-icons.s3-us-west-1.amazonaws.com/material/config.json"


// cached icons configs
let CONFIGS: Map<string, IconConfig>
export function IconsLoader() {
const [configs, setConfigs] = useState<Map<string, IconConfig>>(undefined);
const [queryTerm, setQueryTerm] = useState<string>(undefined);

useEffect(() => {
if (CONFIGS) {
setConfigs(CONFIGS)
} else {
fetch(CONFIG_JSON_S3)
.then((response) => response.json())
.then((json) => {
CONFIGS = json
setConfigs(json)
})
}
}, []);


let list;
if (configs) {
const MAX_PER_LOAD = 100
const validQueryTerm = queryTerm !== undefined && queryTerm.length >= 2
const searchOnlyDefaults: boolean = !validQueryTerm
const defaultIcons = filterIcons(configs, {
onlyDefault: searchOnlyDefaults,
includes: validQueryTerm ? queryTerm : undefined
}).slice(0, MAX_PER_LOAD)
list = <IconList icons={defaultIcons} />
} else {
list = <LinearProgress />
}

return (
<>
<IconSearch onChange={setQueryTerm} />
{list}
</>
)
}

function IconSearch(props: {
onChange: (value: string) => void
}) {
return (
<TextField id="standard-basic" label="search with icon name" onChange={(e) => props.onChange(e.target.value)} />
)
}

function filterIcons(configs: Map<string, IconConfig>, options: {
onlyDefault: boolean,
includes?: string
}): [string, IconConfig][] {
const keys = Object.keys(configs)
const defaultIcons = keys.map<[string, IconConfig]>((k) => {
const item = configs[k] as IconConfig
if (options.onlyDefault) {
if (item.variant != 'default') {
return undefined
}
}

if (options.includes) {
if (k.includes(options.includes)) {
return [k, item]
} else {
return
}
}
return [k, item]

}).filter((k) => k !== undefined)
console.log('default icons', defaultIcons.length)
return defaultIcons
}

function IconList(props: {
icons: [string, IconConfig][]
}) {
const { icons } = props

return <>
<GridList cellHeight={160} cols={5}>
{
icons.map((i) => {
const key = i[0]
const config = i[1]
return <GridListTile key={key}>
<IconItem key={key} name={key} config={config} />
</GridListTile>
})
}
</GridList>
</>
}

function IconItem(props: {
name: string,
config: IconConfig
}) {
const { name, config } = props

const onClick = () => {
console.log(name, 'clicked')
loadSvg(name, config).then((s) => {
console.log('postmessage', EK_CREATE_ICON)
parent.postMessage({
pluginMessage: {
type: EK_CREATE_ICON,
data: {
key: name,
svg: s
}
}
}, "*")
})
}

return <div>
<Tooltip title={`${name} (${config.variant})`}>
<IconButton aria-label="delete" onClick={onClick}>
<svg width="24" height="24">
<image xlinkHref={makeIconUrl(name, config)} width="24" height="24" />
</svg>
</IconButton>
</Tooltip>
</div>
}
3 changes: 2 additions & 1 deletion figma/src/app/constants/ek.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export const EK_GENERATED_CODE_PLAIN = "EK_GENERATED_CODE_PLAIN"
export const EK_IMAGE_ASSET_REPOSITORY_MAP = "EK_IMAGE_ASSET_REPOSITORY_MAP"
export const EK_PREVIEW_SOURCE = "EK_PREVIEW_SOURCE"
export const EK_COPIED = "EK_COPIED";
export const EK_FOCUS_REQUEST = "EK_FOCUS_REQUEST"
export const EK_FOCUS_REQUEST = "EK_FOCUS_REQUEST"
export const EK_CREATE_ICON = "EK_CREATE_ICON"
9 changes: 3 additions & 6 deletions figma/src/app/screens/code-screen.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Widget } from "@bridged.xyz/flutter-builder/lib";
import Button from "@material-ui/core/Button";
import * as React from "react";
import { TransportableImageRepository } from "../../assets-repository";
import { ImageHostingRepository } from "../../assets-repository/hosting";
Expand Down Expand Up @@ -73,12 +74,8 @@ export class CodeScreen extends React.Component<any, State> {
return <div>
<Preview data={(this.state as any).previewImage} name={(this.state as any).name}></Preview>
<Highlight language="dart" app={this.state.app} code={(this.state).code} widget={(this.state).widget} ></Highlight>
<button onClick={this.onClickReportIssue}>
report issue
</button>
<button onClick={this.onClickVisitWebsite}>
visit website
</button>
<Button onClick={this.onClickReportIssue}>report issue</Button>
<Button onClick={this.onClickVisitWebsite}>visit website</Button>
</div>
}
}
5 changes: 5 additions & 0 deletions figma/src/app/screens/icons-screen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from "react"
import { IconsLoader } from "../components/icons-loader"
export function IconsScreen() {
return <IconsLoader />
}
42 changes: 39 additions & 3 deletions figma/src/assets-repository/icons-generator/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
import { IconConfig } from "@bridged.xyz/client-sdk/lib"
import Axios from "axios"
export function insertMaterialIcon(name: string, data: string): FrameNode {
console.log(`inserting icon with name ${name} and data ${data}`)

export function insertMaterialIcon(name: string): FrameNode {
const svg = "" // todo load via name
return figma.createNodeFromSvg(svg)
const currentViewportLocation = figma.viewport.center
const node = figma.createNodeFromSvg(data)
// todo replace naming creation with reflect core
node.name = `icons/mdi_${name}`
node.setSharedPluginData('icon', 'key', name)
node.x = currentViewportLocation.x
node.y = currentViewportLocation.y
return node
}


export async function loadSvg(key: string, config: IconConfig): Promise<string> {
// let header = new Headers({
// 'Access-Control-Allow-Origin':'*',
// 'Content-Type': 'multipart/form-data'
// });
// let sentData={
// method:'GET',
// mode: 'cors',
// header: header,
// };

const url = makeIconUrl(key, config)
const raw = await (await Axios.get(url, {
headers: {
'Access-Control-Allow-Origin': '*',
}
})).data
// const raw = await (await fetch(, sentData)).text()
console.log(`icon raw data loaded for ${key}, length of ${raw.length}`)
return raw
}

export function makeIconUrl(name: string, config: IconConfig): string {
return `https://reflect-icons.s3-us-west-1.amazonaws.com/${config.host}/${name}.svg`
}
12 changes: 11 additions & 1 deletion figma/src/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { buildApp } from "./flutter";
import { retrieveFlutterColors } from "./flutter/utils/fetch-colors";
import { hideAllExcept, hideAllOnly } from "./dev-tools/hide-all";
import { runLints } from "./lint/lint";
import { EK_COPIED, EK_FOCUS_REQUEST, EK_GENERATED_CODE_PLAIN, EK_IMAGE_ASSET_REPOSITORY_MAP, EK_LINT_FEEDBACK, EK_PREVIEW_SOURCE } from "./app/constants/ek.constant";
import { EK_COPIED, EK_CREATE_ICON, EK_FOCUS_REQUEST, EK_GENERATED_CODE_PLAIN, EK_IMAGE_ASSET_REPOSITORY_MAP, EK_LINT_FEEDBACK, EK_PREVIEW_SOURCE } from "./app/constants/ek.constant";
import { handleNotify } from "@bridged.xyz/design-sdk/lib/figma";
import { makeApp } from "./flutter/make/app.make";
import { ImageRepositories } from "./assets-repository";
import { insertMaterialIcon } from "./assets-repository/icons-generator";


let parentNodeId: string;
Expand Down Expand Up @@ -146,6 +147,7 @@ figma.on("selectionchange", () => {
// efficient? No. Works? Yes.
// todo pass data instead of relying in types
figma.ui.onmessage = (msg) => {
console.log('event received', msg)
handleNotify(msg)
// region test
if (msg.type === 'create-rectangles') {
Expand All @@ -169,6 +171,14 @@ figma.ui.onmessage = (msg) => {
figma.viewport.scrollAndZoomIntoView([target]);
}

else if (msg.type == EK_CREATE_ICON) {

const icon_key = msg.data.key
const svgData = msg.data.svg
const inserted = insertMaterialIcon(icon_key, svgData)
figma.viewport.scrollAndZoomIntoView([inserted])
}

else if (msg.type == "randomize-selection") {
randimizeText()
}
Expand Down
7 changes: 5 additions & 2 deletions figma/src/flutter/interpreter/icon.interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import { ImageRepositories, TemporaryImageAsset } from "../../assets-repository"
export function interpretIcon(node: ReflectSceneNode): IconData | TemporaryImageAsset {

try {
const name = node.name.match(/(?<=mdi_)(.*?)*/g)[0]
console.log('mdi matching name found', name)
// regex is valid, but does not work at this point. inspect this, make it live again.
// const re = /(?<=mdi_)(.*?)*/g // finds **mdi_** pattern
const splits = node.name.split('mdi_')
const name = splits[splits.length - 1]
console.log(`mdi matching name found, ${JSON.stringify(name)}`)
const mdicon = Icons.fromName(name)
return mdicon
} catch (e) {
Expand Down
4 changes: 3 additions & 1 deletion figma/src/flutter/make/icon.make.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export function makeDynamicIcon(node: ReflectSceneNode): Icon | Image {
width: node.width,
height: node.height,
fit: BoxFit.cover as Snippet
}).addComment(`FIXME: Check your design. this is an icon of node ${node.toString()}. we couldn't any matching flutter native icon, so we uploaded the asset to the cloud, load from it.`)
})
.addComment('Detected as Icon')
.addComment(`FIXME: Check your design. this is an icon of node ${node.toString()}. we couldn't any matching flutter native icon, so we uploaded the asset to the cloud, load from it.`)
}
}

Expand Down
Loading

0 comments on commit 10fa683

Please sign in to comment.