5
5
import {
6
6
BlockGrayLabel ,
7
7
ControlPropertyViewWrapper ,
8
+ CustomModal ,
8
9
DeleteInputIcon ,
9
10
TacoButton ,
10
11
TacoInput ,
@@ -25,6 +26,9 @@ import { getBase64 } from "@lowcoder-ee/util/fileUtils";
25
26
import Flex from "antd/es/flex" ;
26
27
import Typography from "antd/es/typography" ;
27
28
import LoadingOutlined from "@ant-design/icons/LoadingOutlined" ;
29
+ import Badge from "antd/es/badge" ;
30
+ import { CrownFilled } from "@ant-design/icons" ;
31
+ import { SUBSCRIPTION_SETTING } from "@lowcoder-ee/constants/routesURL" ;
28
32
29
33
const ButtonWrapper = styled . div `
30
34
width: 100%;
@@ -125,7 +129,7 @@ const IconList = styled(List)`
125
129
` ;
126
130
127
131
const IconRow = styled . div `
128
- padding: 0 6px;
132
+ padding: 6px;
129
133
display: flex;
130
134
align-items: flex-start; /* Align items to the start to allow different heights */
131
135
justify-content: space-between;
@@ -134,37 +138,56 @@ const IconRow = styled.div`
134
138
gap: 8px;
135
139
justify-content: flex-start;
136
140
}
141
+
142
+ .ant-badge {
143
+ height: 100%;
144
+ }
137
145
` ;
138
146
139
147
const IconItemContainer = styled . div `
140
148
width: 60px;
149
+ height: 60px;
141
150
display: flex;
142
151
flex-direction: column;
143
152
align-items: center;
144
153
justify-content: flex-start;
145
154
cursor: pointer;
146
155
font-size: 28px;
147
- margin-bottom: 24px;
156
+ border-radius: 4px;
157
+ background: #fafafa;
148
158
149
159
&:hover {
150
- border: 1px solid #315efb;
151
- border-radius: 4px;
160
+ box-shadow: 0 8px 24px #1a29470a,0 2px 8px #1a294714;
152
161
}
153
162
154
163
&:focus {
155
164
border: 1px solid #315efb;
156
- border-radius: 4px;
157
165
box-shadow: 0 0 0 2px #d6e4ff;
158
166
}
159
167
` ;
160
168
161
- const IconWrapper = styled . div `
162
- height: auto ;
169
+ const IconWrapper = styled . div < { $isPremium ?: boolean } > `
170
+ height: 100% ;
163
171
display: flex;
164
172
align-items: center;
165
173
justify-content: center;
174
+ ${ props => props . $isPremium && 'opacity: 0.25' } ;
175
+ ` ;
176
+
177
+ const StyledPreviewIcon = styled . img `
178
+ width: auto;
179
+ height: auto;
180
+ max-width: 100%;
181
+ max-height: 100%;
166
182
` ;
167
183
184
+ const StyledPreviewLotte = styled . video `
185
+ width: auto;
186
+ height: auto;
187
+ max-width: 100%;
188
+ max-height: 100%;
189
+ `
190
+
168
191
export enum AssetType {
169
192
ICON = "icon" ,
170
193
ILLUSTRATION = "illustration" ,
@@ -182,9 +205,8 @@ const IconScoutSearchParams: SearchParams = {
182
205
query : '' ,
183
206
product_type : 'item' ,
184
207
asset : 'icon' ,
185
- per_page : 50 ,
208
+ per_page : 25 ,
186
209
page : 1 ,
187
- formats : 'svg' ,
188
210
sort : 'relevant' ,
189
211
} ;
190
212
@@ -216,13 +238,20 @@ export const IconPicker = (props: {
216
238
217
239
const fetchResults = async ( query : string ) => {
218
240
setLoading ( true ) ;
219
- const result = await IconscoutApi . search ( {
241
+ const freeResult = await IconscoutApi . search ( {
220
242
...IconScoutSearchParams ,
221
243
asset : props . assetType ,
244
+ price : 'free' ,
245
+ query,
246
+ } ) ;
247
+ const premiumResult = await IconscoutApi . search ( {
248
+ ...IconScoutSearchParams ,
249
+ asset : props . assetType ,
250
+ price : 'premium' ,
222
251
query,
223
252
} ) ;
224
253
setLoading ( false ) ;
225
- setSearchResults ( result . data ) ;
254
+ setSearchResults ( [ ... freeResult . data , ... premiumResult . data ] ) ;
226
255
} ;
227
256
228
257
const downloadAsset = async (
@@ -271,33 +300,53 @@ export const IconPicker = (props: {
271
300
key = { icon . uuid }
272
301
tabIndex = { 0 }
273
302
onClick = { ( ) => {
303
+ // check if premium content then show subscription popup
304
+ // TODO: if user has subscription then skip this if block
305
+ if ( icon . price !== 0 ) {
306
+ CustomModal . confirm ( {
307
+ title : trans ( "iconScout.buySubscriptionTitle" ) ,
308
+ content : trans ( "iconScout.buySubscriptionContent" ) ,
309
+ onConfirm : ( ) => {
310
+ window . open ( SUBSCRIPTION_SETTING , "_blank" ) ;
311
+ } ,
312
+ confirmBtnType : "primary" ,
313
+ okText : trans ( "iconScout.buySubscriptionButton" ) ,
314
+ } )
315
+ return ;
316
+ }
317
+
274
318
fetchDownloadUrl (
275
319
icon . uuid ,
276
320
props . assetType === AssetType . ICON ? icon . urls . png_64 : icon . urls . thumb ,
277
321
) ;
278
322
} }
279
323
>
280
- < IconWrapper >
281
- { props . assetType === AssetType . ICON && (
282
- < img style = { { 'width' : '100%' } } src = { icon . urls . png_64 } />
283
- ) }
284
- { props . assetType === AssetType . ILLUSTRATION && (
285
- < img style = { { 'width' : '100%' } } src = { icon . urls . thumb } />
286
- ) }
287
- { props . assetType === AssetType . LOTTIE && (
288
- < video style = { { 'width' : '100%' } } src = { icon . urls . thumb } autoPlay />
289
- ) }
290
- </ IconWrapper >
324
+ < Badge
325
+ count = { icon . price !== 0 ? < CrownFilled style = { { color : "#e7b549" } } /> : undefined }
326
+ size = 'small'
327
+ >
328
+ < IconWrapper $isPremium = { icon . price !== 0 } >
329
+ { props . assetType === AssetType . ICON && (
330
+ < StyledPreviewIcon src = { icon . urls . png_64 } />
331
+ ) }
332
+ { props . assetType === AssetType . ILLUSTRATION && (
333
+ < StyledPreviewIcon src = { icon . urls . thumb } />
334
+ ) }
335
+ { props . assetType === AssetType . LOTTIE && (
336
+ < StyledPreviewLotte src = { icon . urls . thumb } autoPlay />
337
+ ) }
338
+ </ IconWrapper >
339
+ </ Badge >
291
340
</ IconItemContainer >
292
341
) ) }
293
342
</ IconRow >
294
343
) , [ searchResults ]
295
344
) ;
296
345
297
346
const popupTitle = useMemo ( ( ) => {
298
- if ( props . assetType === AssetType . ILLUSTRATION ) return 'Search Image' ;
299
- if ( props . assetType === AssetType . LOTTIE ) return 'Search Animation' ;
300
- return 'Search Icon' ;
347
+ if ( props . assetType === AssetType . ILLUSTRATION ) return trans ( "iconScout.searchImage" ) ;
348
+ if ( props . assetType === AssetType . LOTTIE ) return trans ( "iconScout.searchAnimation" ) ;
349
+ return trans ( "iconScout.searchIcon" ) ;
301
350
} , [ props . assetType ] ) ;
302
351
303
352
return (
@@ -337,7 +386,7 @@ export const IconPicker = (props: {
337
386
{ ! loading && Boolean ( searchText ) && ! searchResults ?. length && (
338
387
< Flex align = "center" justify = "center" style = { { flex : 1 } } >
339
388
< Typography . Text type = "secondary" >
340
- No results found.
389
+ { trans ( "iconScout.noResults" ) }
341
390
</ Typography . Text >
342
391
</ Flex >
343
392
) }
0 commit comments