Skip to content

Commit 505cfe3

Browse files
kaeizenbfintal
andauthored
Feat: Icon Library (#3317)
* add icon library in global settings * change ProControl type * create reusable component * add icons to icon library * improve code readability * updated premium notice text * update popover * add debounce prevent frequent api calls when changing item names * fix remaining issues * if show premium notices are disabled, also hide this --------- Co-authored-by: Benjamin Intal <[email protected]> Co-authored-by: [email protected] <>
1 parent 47cdbe1 commit 505cfe3

File tree

11 files changed

+390
-185
lines changed

11 files changed

+390
-185
lines changed

src/components/font-awesome-icon/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ const addSVGAttributes = ( svgHTML, attributesToAdd = {}, attributesToRemove = [
8080
}
8181

8282
const FontAwesomeIcon = memo( props => {
83+
const {
84+
svgAttrsToAdd = { width: '32', height: '32' },
85+
svgAttrsToRemove = [ 'id', 'data-name' ],
86+
} = props
8387
const [ forceUpdateCount, setForceUpdateCount ] = useState( 0 )
8488
const forceUpdate = () => {
8589
setForceUpdateCount( forceUpdateCount + 1 )
@@ -91,7 +95,7 @@ const FontAwesomeIcon = memo( props => {
9195
if ( typeof props.value === 'string' && props.value.match( /^<svg/ ) ) {
9296
let svg = addSVGAriaLabel( props.value, props.ariaLabel )
9397
// Add fallback SVG width and height values.
94-
svg = addSVGAttributes( svg, { width: '32', height: '32' } )
98+
svg = addSVGAttributes( svg, svgAttrsToAdd, svgAttrsToRemove )
9599
return <RawHTML { ...propsToPass }>{ props.prependRenderString + svg }</RawHTML>
96100
}
97101

@@ -109,7 +113,7 @@ const FontAwesomeIcon = memo( props => {
109113

110114
let svg = addSVGAriaLabel( iconHTML, props.ariaLabel )
111115
// Add fallback SVG width and height values.
112-
svg = addSVGAttributes( svg, { width: '32', height: '32' } )
116+
svg = addSVGAttributes( svg, svgAttrsToAdd, svgAttrsToRemove )
113117
return <RawHTML { ...propsToPass }>{ props.prependRenderString + svg }</RawHTML>
114118
}
115119

@@ -123,7 +127,7 @@ const FontAwesomeIcon = memo( props => {
123127

124128
let svg = addSVGAriaLabel( iconHTML, props.ariaLabel )
125129
// Add fallback SVG width and height values.
126-
svg = addSVGAttributes( svg, { width: '32', height: '32' } )
130+
svg = addSVGAttributes( svg, svgAttrsToAdd, svgAttrsToRemove )
127131
return <RawHTML { ...propsToPass }>{ props.prependRenderString + svg }</RawHTML>
128132
} )
129133

src/components/icon-search-popover/index.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import {
1111
PanelBody, TextControl, Spinner,
1212
} from '@wordpress/components'
1313
import { __ } from '@wordpress/i18n'
14-
import { useState, useEffect } from '@wordpress/element'
14+
import {
15+
useState, useEffect, Fragment,
16+
} from '@wordpress/element'
1517

1618
/**
1719
* External dependencies
@@ -25,6 +27,7 @@ import {
2527
import { faGetIcon, faFetchIcon } from '~stackable/util'
2628
import { FileDrop } from 'react-file-drop'
2729
import classnames from 'classnames'
30+
import { applyFilters, doAction } from '@wordpress/hooks'
2831

2932
let searchTimeout = null
3033
let tempMediaUpload = null
@@ -83,7 +86,7 @@ export const cleanSvgString = svgString => {
8386

8487
const IconSearchPopover = props => {
8588
const [ value, setValue ] = useState( '' )
86-
const [ results, setResults ] = useState( [] )
89+
const [ results, setResults ] = useState( { faIcons: [], iconLibrary: [] } )
8790
const [ isBusy, setIsBusy ] = useState( false )
8891
const [ isDropping, setIsDropping ] = useState( false )
8992

@@ -151,6 +154,9 @@ const IconSearchPopover = props => {
151154
fr.onload = function( e ) {
152155
setIsDropping( false )
153156
const svgString = cleanSvgString( addCustomIconClass( e.target.result ) )
157+
158+
doAction( 'stackable.icon-search-popover.svg-upload', svgString )
159+
154160
props.onChange( svgString )
155161
props.onClose()
156162
}
@@ -168,6 +174,8 @@ const IconSearchPopover = props => {
168174
'ugb-icon--has-reset': props.allowReset,
169175
} )
170176

177+
const IconLibraryIcons = applyFilters( 'stackable.global-settings.inspector.icon-library.icons', Fragment )
178+
171179
const content = (
172180
<div className="stk-icon-search-popover-container">
173181
<FileDrop
@@ -191,6 +199,8 @@ const IconSearchPopover = props => {
191199
fr.onload = function( e ) {
192200
setIsDropping( false )
193201
const svgString = cleanSvgString( addCustomIconClass( e.target.result ) )
202+
203+
doAction( 'stackable.icon-search-popover.svg-upload', svgString )
194204
props.onChange( svgString )
195205
props.onClose()
196206
}
@@ -244,13 +254,26 @@ const IconSearchPopover = props => {
244254
</div>
245255
<div className="ugb-icon-popover__iconlist">
246256
{ isBusy && <Spinner /> }
247-
{ ! isBusy && results.map( ( { prefix, iconName }, i ) => {
257+
{ ! isBusy && <IconLibraryIcons
258+
icons={ results.iconLibrary }
259+
onChange={ props.onChange }
260+
onClose={ props.onClose }
261+
/> }
262+
{ ! isBusy && results.faIcons.map( ( { prefix, iconName }, i ) => {
248263
const iconValue = `${ prefix }-${ iconName }`
249264
return <button
250265
key={ i }
251266
className={ `components-button ugb-prefix--${ prefix } ugb-icon--${ iconName }` }
252267
onClick={ async () => {
253-
if ( props.returnSVGValue ) {
268+
if ( props.returnSVGValue && props.returnIconName ) {
269+
let svgIcon = faGetIcon( prefix, iconName )
270+
271+
if ( ! svgIcon ) {
272+
await faFetchIcon( prefix, iconName )
273+
svgIcon = faGetIcon( prefix, iconName )
274+
}
275+
props.onChange( cleanSvgString( svgIcon ), prefix, iconName )
276+
} else if ( props.returnSVGValue ) {
254277
let svgIcon = faGetIcon( prefix, iconName )
255278

256279
if ( ! svgIcon ) {
@@ -267,7 +290,7 @@ const IconSearchPopover = props => {
267290
<FontAwesomeIcon prefix={ prefix } iconName={ iconName } />
268291
</button>
269292
} ) }
270-
{ ! isBusy && ! results.length &&
293+
{ ! isBusy && ! results.faIcons.length && ! results.iconLibrary.length &&
271294
<p className="components-base-control__help">{ __( 'No matches found', i18n ) }</p>
272295
}
273296
</div>

src/components/icon-search-popover/search.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { applyFilters } from '@wordpress/hooks'
12
import {
23
fontAwesomeSearchProIcons, iconsFaKit, iconsFaProKitVersion, iconsFaFreeKitVersion,
34
} from 'stackable'
@@ -23,13 +24,17 @@ export const searchFontAwesomeIconName = async ( name = 'icon', isPro = fontAwes
2324
} )
2425
.then( r => r.json() )
2526

26-
return data.data.search.reduce( ( iconResults, iconData ) => {
27+
const faIcons = data.data.search.reduce( ( iconResults, iconData ) => {
2728
convertFontAwesomeToIcon( iconData, isPro ).forEach( icon => {
2829
iconResults.push( icon )
2930
} )
3031

3132
return iconResults
3233
}, [] )
34+
35+
const iconLibrary = applyFilters( 'stackable.global-settings.inspector.icon-library.search-icons', null, name )
36+
37+
return { faIcons, iconLibrary }
3338
}
3439

3540
/**

src/components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,4 @@ export { default as ColumnsWidthMultiControl } from './columns-width-multi-contr
115115
export { default as Popover } from './popover'
116116
export { default as HelpTooltip } from './help-tooltip'
117117
export { default as RichText } from './rich-text'
118+
export { default as SortablePicker } from './sortable-picker'

src/components/pro-control/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ const LABELS = {
109109
<li>{ __( 'Hide the current post - great for synced patterns', i18n ) }</li>
110110
</ul>,
111111
},
112+
'icon-library': {
113+
title: __( 'Unlock Your Icon Library', i18n ),
114+
description: <ul>
115+
<li>{ __( 'Add your custom SVG icons', i18n ) }</li>
116+
<li>{ __( 'Easily access your custom icons in the icon picker', i18n ) }</li>
117+
<li>{ __( 'Organize your custom icons in your library', i18n ) }</li>
118+
</ul>,
119+
},
112120
}
113121

114122
const ProControl = props => {

0 commit comments

Comments
 (0)