feat: filters support FeColorMatrix and FilterImage (#2316)

# Summary

Introducing the long-awaited **Filters** in `react-native-svg` 🎉

### Motivation

This PR is the beginning of bringing support of SVG Filters into
`react-native-svg`.

* **related issues**: This PR series will address the following issues:
#150, #176, #635, #883, #994, #996, #1216
* **feature overview**: This PR is a boilerplate for Filters
   * introducing `Filter` component and `FeColorMatrix` as a start. 
* It also introduces a new subdirectory called
`react-native-svg/filter-image` with a `FilterImage` component.

# Usage

## Filter and Fe...

Filters are compatible with the web familiar standard, so most things
should be compatible out-of-the-box and changes will be limited to using
a capital letter as it's component.

### Example

```tsx
import React from 'react';
import { FeColorMatrix, Filter, Rect, Svg } from 'react-native-svg';

export default () => {
  return (
    <Svg height="300" width="300">
      <Filter id="myFilter">
        <FeColorMatrix type="saturate" values="0.2" />
      </Filter>
      <Rect
        x="0"
        y="0"
        width="300"
        height="300"
        fill="red"
        filter="url(#myFilter)"
      />
    </Svg>
  );
};
```

![image](https://github.com/software-mansion/react-native-svg/assets/39670088/c36fb238-95f4-455d-b0aa-2a7d4038b828)

## Filter Image

`FilterImage` is a new component that is not strictly related to SVG.
Its behavior should be the same as a regular `Image` component from
React Native with one exception - the additional prop `filters`, which
accepts an array of filters to apply to the image.

### Example

```tsx
import React from 'react';
import { StyleSheet } from 'react-native';
import { FilterImage } from 'react-native-svg/filter-image';

const myImage = require('./myImage.jpg');

export default () => {
  return (
    <FilterImage
      style={styles.image}
      source={myImage}
      filters={[
        { name: 'colorMatrix', type: 'saturate', values: 0.2 },
        {
          name: 'colorMatrix',
          type: 'matrix',
          values: [
            0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0, 0,
            0, 1, 0,
          ],
        },
      ]}
    />
  );
};
const styles = StyleSheet.create({
  image: {
    width: 200,
    height: 200,
  },
});
```


![image](https://github.com/software-mansion/react-native-svg/assets/39670088/666ed89f-68d8-491b-b97f-1eef112b7095)

## Test Plan

**Example App**: Updated the example app with various filter effects,
showcasing real-world usage.

## Compatibility

| OS      | Implemented |
| ------- | :---------: |
| iOS     |         |
| Android |         |

## Checklist

- [x] I have tested this on a device and a simulator
- [x] I added documentation in `README.md` and `USAGE.md`
- [x] I updated the typed files (typescript)
This commit is contained in:
Jakub Grzywacz
2024-07-11 11:17:35 +02:00
committed by GitHub
parent 1de7709176
commit 08e92074b4
135 changed files with 3046 additions and 322 deletions

View File

@@ -13,86 +13,13 @@ import {
ScrollView,
TouchableHighlight,
TouchableOpacity,
SafeAreaView,
} from 'react-native';
import {Modal, Platform} from 'react-native';
import {Svg, Circle, Line} from 'react-native-svg';
import * as examples from './src/examples';
const hairline = StyleSheet.hairlineWidth;
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 20,
alignItems: 'center',
overflow: 'hidden',
},
contentContainer: {
alignSelf: 'stretch',
borderTopWidth: hairline,
borderTopColor: '#ccc',
borderBottomWidth: hairline,
borderBottomColor: '#ccc',
flexWrap: 'wrap',
flexDirection: 'row',
marginHorizontal: 10,
},
welcome: {
padding: 10,
color: '#f60',
fontSize: 18,
fontWeight: 'bold',
},
link: {
height: 40,
alignSelf: 'stretch',
width: Dimensions.get('window').width / 2 - 10,
},
title: {
marginLeft: 10,
},
cell: {
height: 40,
paddingHorizontal: 10,
alignSelf: 'stretch',
alignItems: 'center',
flexDirection: 'row',
borderTopWidth: hairline,
borderTopColor: '#ccc',
marginTop: -hairline,
backgroundColor: 'transparent',
},
close: {
position: 'absolute',
right: 20,
top: 40,
},
scroll: {
position: 'absolute',
top: 30,
right: 10,
bottom: 20,
left: 10,
backgroundColor: '#fff',
},
scrollContent: {
borderTopWidth: hairline,
borderTopColor: '#ccc',
},
example: {
paddingVertical: 25,
alignSelf: 'stretch',
alignItems: 'center',
borderBottomWidth: hairline,
borderBottomColor: '#ccc',
},
sampleTitle: {
marginHorizontal: 15,
fontSize: 16,
color: '#666',
},
});
import {commonStyles} from './src/commonStyles';
const names: (keyof typeof examples)[] = [
'Svg',
@@ -116,6 +43,8 @@ const names: (keyof typeof examples)[] = [
'Transforms',
'Markers',
'Mask',
'Filters',
'FilterImage',
];
const initialState = {
@@ -142,8 +71,8 @@ export default class SvgExample extends Component {
content: (
<View>
{samples.map((Sample, i) => (
<View style={styles.example} key={`sample-${i}`}>
<Text style={styles.sampleTitle}>{Sample.title}</Text>
<View style={commonStyles.example} key={`sample-${i}`}>
<Text style={commonStyles.sampleTitle}>{Sample.title}</Text>
<Sample />
</View>
))}
@@ -171,9 +100,9 @@ export default class SvgExample extends Component {
underlayColor="#ccc"
key={`example-${name}`}
onPress={() => this.show(name)}>
<View style={styles.cell}>
<View style={commonStyles.cell}>
{icon}
<Text style={styles.title}>{name}</Text>
<Text style={commonStyles.title}>{name}</Text>
</View>
</TouchableHighlight>
);
@@ -182,13 +111,15 @@ export default class SvgExample extends Component {
modalContent = () => (
<>
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.scrollContent}
scrollEnabled={this.state.scroll}>
{this.state.content}
</ScrollView>
<View style={styles.close}>
<SafeAreaView style={{flex: 1}}>
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.scrollContent}
scrollEnabled={this.state.scroll}>
{this.state.content}
</ScrollView>
</SafeAreaView>
<SafeAreaView style={styles.close}>
<TouchableOpacity activeOpacity={0.7} onPress={this.hide}>
<Svg height="20" width="20">
<Circle cx="10" cy="10" r="10" fill="red" />
@@ -196,14 +127,14 @@ export default class SvgExample extends Component {
<Line x1="4" y1="16" x2="16" y2="4" stroke="#fff" strokeWidth="2" />
</Svg>
</TouchableOpacity>
</View>
</SafeAreaView>
</>
);
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>SVG library for React Apps</Text>
<SafeAreaView style={styles.container}>
<Text style={commonStyles.welcome}>SVG library for React Apps</Text>
<View style={styles.contentContainer}>{this.getExamples()}</View>
{(Platform.OS === 'windows' || Platform.OS === 'macos') &&
this.state.modal ? (
@@ -217,7 +148,50 @@ export default class SvgExample extends Component {
{this.modalContent()}
</Modal>
)}
</View>
</SafeAreaView>
);
}
}
const hairline = StyleSheet.hairlineWidth;
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 20,
alignItems: 'center',
overflow: 'hidden',
},
contentContainer: {
alignSelf: 'stretch',
borderTopWidth: hairline,
borderTopColor: '#ccc',
borderBottomWidth: hairline,
borderBottomColor: '#ccc',
flexWrap: 'wrap',
flexDirection: 'row',
marginHorizontal: 10,
},
link: {
height: 40,
alignSelf: 'stretch',
width: Dimensions.get('window').width / 2 - 10,
},
close: {
position: 'absolute',
right: 20,
top: 20,
},
scroll: {
position: 'absolute',
top: 30,
right: 10,
bottom: 20,
left: 10,
backgroundColor: '#fff',
},
scrollContent: {
borderTopWidth: hairline,
borderTopColor: '#ccc',
},
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -0,0 +1,41 @@
import {StyleSheet} from 'react-native';
const hairline = StyleSheet.hairlineWidth;
export const commonStyles = StyleSheet.create({
welcome: {
padding: 10,
color: '#f60',
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
link: {
height: 40,
},
title: {
marginLeft: 10,
},
cell: {
height: 40,
paddingHorizontal: 10,
alignSelf: 'stretch',
alignItems: 'center',
flexDirection: 'row',
borderTopWidth: hairline,
borderTopColor: '#ccc',
marginTop: -hairline,
},
example: {
paddingVertical: 25,
alignSelf: 'stretch',
alignItems: 'center',
borderBottomWidth: hairline,
borderBottomColor: '#ccc',
},
sampleTitle: {
marginHorizontal: 15,
fontSize: 16,
color: '#666',
},
});

View File

@@ -19,6 +19,8 @@ import * as Reanimated from './examples/Reanimated';
import * as Transforms from './examples/Transforms';
import * as Markers from './examples/Markers';
import * as Mask from './examples/Mask';
import * as Filters from './examples/Filters';
import * as FilterImage from './examples/FilterImage';
export {
Svg,
@@ -42,4 +44,6 @@ export {
Transforms,
Markers,
Mask,
Filters,
FilterImage,
};

View File

@@ -0,0 +1,142 @@
import React, {useState} from 'react';
import {
Dimensions,
FlatList,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {FilterImage, Filters} from 'react-native-svg/filter-image';
const img = require('../../assets/office.jpg');
const normal: Filters = [];
const losAngeles: Filters = [
{
name: 'colorMatrix',
type: 'matrix',
values: [1.8, 0, 0, 0, 0, 0, 1.3, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0],
},
];
const lagos: Filters = [
{
name: 'colorMatrix',
type: 'matrix',
values: [
1.4, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.5, 0, 0, 0, 0, 0, 0.9, 0,
],
},
];
const tokyo: Filters = [
{name: 'colorMatrix', type: 'saturate', values: [1.5]},
{
name: 'colorMatrix',
type: 'matrix',
values: [
0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 1,
0,
],
},
];
const saturated: Filters = [
{name: 'colorMatrix', type: 'saturate', values: [1.5]},
];
const boring: Filters = [
{
name: 'colorMatrix',
type: 'matrix',
values: [
0.6965, 0.3845, 0.0945, 0, 0, 0.1745, 0.8430000000000001, 0.084, 0, 0,
0.136, 0.267, 0.5655, 0, 0, 0, 0, 0, 1, 0,
],
},
];
const filters = {
normal,
losAngeles,
lagos,
tokyo,
saturated,
boring,
} as const;
type FilterKeys =
| 'normal'
| 'losAngeles'
| 'lagos'
| 'tokyo'
| 'saturated'
| 'boring';
const filterKeys = Object.keys(filters) as FilterKeys[];
const FilterImagePickerExample = () => {
const [currentFilter, setCurrentFilter] = useState<FilterKeys>('normal');
return (
<View style={styles.container}>
<FilterImage
style={styles.image}
source={img}
filters={filters[currentFilter]}
/>
<View>
<FlatList
data={filterKeys}
horizontal
style={styles.list}
contentContainerStyle={styles.listElement}
renderItem={({item}) => {
return (
<TouchableOpacity onPress={() => setCurrentFilter(item)}>
<FilterImage
style={styles.listElementImage}
source={img}
filters={filters[item]}
/>
<Text numberOfLines={1} style={styles.listElementTitle}>
{item}
</Text>
</TouchableOpacity>
);
}}
/>
</View>
</View>
);
};
FilterImagePickerExample.title = 'Filter picker';
const styles = StyleSheet.create({
container: {
flex: 1,
height: Dimensions.get('window').height - 150,
width: '100%',
},
image: {flex: 1, width: '100%', height: '100%'},
list: {
marginTop: 8,
marginHorizontal: 8,
},
listElement: {gap: 8},
listElementImage: {width: 70, height: 70},
listElementTitle: {
width: 70,
textAlign: 'center',
marginTop: 2,
marginBottom: 8,
},
});
const icon = (
<FilterImage
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
source={img}
width={30}
height={30}
/>
);
const samples = [FilterImagePickerExample];
export {icon, samples};

View File

@@ -0,0 +1,29 @@
import React from 'react';
import {View} from 'react-native';
import {FilterImage} from 'react-native-svg/filter-image';
const testImage = require('../../assets/image.jpg');
const FilterImageLocalExample = () => {
return (
<View>
<FilterImage
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
source={testImage}
/>
</View>
);
};
FilterImageLocalExample.title = 'Local image with filter';
const icon = (
<FilterImage
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
source={testImage}
width={30}
height={30}
/>
);
const samples = [FilterImageLocalExample];
export {icon, samples};

View File

@@ -0,0 +1,52 @@
import React from 'react';
import {View} from 'react-native';
import {FilterImage} from 'react-native-svg/filter-image';
const testSource = {
uri: 'https://cdn.pixabay.com/photo/2023/03/17/11/39/mountain-7858482_1280.jpg',
};
const FilterImageRemoteExample = () => {
return (
<View>
<FilterImage
filters={[{name: 'colorMatrix', type: 'saturate', values: [3]}]}
source={testSource}
style={{width: 200, height: 200}}
/>
</View>
);
};
FilterImageRemoteExample.title = 'Remote image with filter';
const FilterImageFewFiltersExample = () => {
return (
<View>
<FilterImage
filters={[
{name: 'colorMatrix', type: 'saturate', values: [10]},
{
name: 'colorMatrix',
type: 'matrix',
values: '0.2 0.2 0.2 0 0 0.2 0.2 0.2 0 0 0.2 0.2 0.2 0 0 0 0 0 1 0',
},
]}
source={testSource}
style={{width: 200, height: 200}}
/>
</View>
);
};
FilterImageFewFiltersExample.title = 'Remote image with filters';
const icon = (
<FilterImage
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
source={testSource}
width={30}
height={30}
/>
);
const samples = [FilterImageRemoteExample, FilterImageFewFiltersExample];
export {icon, samples};

View File

@@ -0,0 +1,4 @@
import * as LocalImage from './LocalImage';
import * as RemoteImage from './RemoteImage';
import * as FilterPicker from './FilterPicker';
export {LocalImage, RemoteImage, FilterPicker};

View File

@@ -0,0 +1,65 @@
import React from 'react';
import {StyleSheet, Text, TouchableHighlight, View} from 'react-native';
import {FilterImage} from 'react-native-svg/filter-image';
import {commonStyles} from '../../commonStyles';
import * as examples from './examples';
const FilterImageList = () => {
const [example, setExample] = React.useState<keyof typeof examples | null>(
null,
);
if (example) {
return (
<>
{examples[example].samples.map((Sample, i) => (
<View style={commonStyles.example} key={`sample-${i}`}>
<Text style={commonStyles.sampleTitle}>{Sample.title}</Text>
<Sample />
</View>
))}
</>
);
}
return (
<View style={styles.container}>
<Text style={commonStyles.welcome}>Filter Image</Text>
{Object.keys(examples).map((element, i) => {
const name = element as keyof typeof examples;
return (
<TouchableHighlight
style={styles.link}
underlayColor="#ccc"
key={`example-${name}`}
onPress={() => setExample(name)}>
<View style={commonStyles.cell}>
{examples[name].icon}
<Text style={commonStyles.title}>{name}</Text>
</View>
</TouchableHighlight>
);
})}
</View>
);
};
FilterImageList.title = '';
const styles = StyleSheet.create({
container: {width: '100%'},
link: {height: 40},
});
const icon = (
<FilterImage
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
source={require('../../assets/image.jpg')}
width={30}
height={30}
/>
);
const samples = [FilterImageList];
export {icon, samples};

View File

@@ -0,0 +1,130 @@
import React, {Component} from 'react';
import {Svg, Circle, FeColorMatrix, Filter, G} from 'react-native-svg';
class ReferenceExample extends Component {
static title = 'Reference';
render() {
return (
<Svg height="150" width="150">
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
</Svg>
);
}
}
class IdentityExample extends Component {
static title = 'Identity matrix';
render() {
return (
<Svg height="150" width="150">
<Filter id="filter">
<FeColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" />
</Filter>
<G filter="url(#filter)">
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
</G>
</Svg>
);
}
}
class RgbToGreenExample extends Component {
static title = 'RGB to Green';
render() {
return (
<Svg height="150" width="150">
<Filter id="filter">
<FeColorMatrix
values="0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 0 0 1 0"
/>
</Filter>
<G filter="url(#filter)">
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
</G>
</Svg>
);
}
}
class SaturateExample extends Component {
static title = 'Saturate';
render() {
return (
<Svg height="150" width="150">
<Filter id="filter">
<FeColorMatrix type="saturate" values="0.2" />
</Filter>
<G filter="url(#filter)">
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
</G>
</Svg>
);
}
}
class HueRotateExample extends Component {
static title = 'Hue Rotate';
render() {
return (
<Svg height="150" width="150">
<Filter id="filter">
<FeColorMatrix type="hueRotate" values="180" />
</Filter>
<G filter="url(#filter)">
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
</G>
</Svg>
);
}
}
class LuminanceToAlphaExample extends Component {
static title = 'Luminance to alpha';
render() {
return (
<Svg height="150" width="150">
<Filter id="filter">
<FeColorMatrix type="luminanceToAlpha" />
</Filter>
<G filter="url(#filter)">
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
</G>
</Svg>
);
}
}
const icon = (
<Svg height="30" width="30" viewBox="0 0 20 20">
<Filter id="filter">
<FeColorMatrix values="0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0" />
</Filter>
<G filter="url(#filter)">
<Circle cx="10" cy="7.5" r="5" fill="blue" fillOpacity="0.5" />
<Circle cx="7.5" cy="12.5" r="5" fill="green" fillOpacity="0.5" />
<Circle cx="12.5" cy="12.5" r="5" fill="red" fillOpacity="0.5" />
</G>
</Svg>
);
const samples = [
ReferenceExample,
IdentityExample,
RgbToGreenExample,
SaturateExample,
HueRotateExample,
LuminanceToAlphaExample,
];
export {icon, samples};

View File

@@ -0,0 +1,56 @@
import React, {useEffect} from 'react';
import Animated, {
AnimatedProps,
useAnimatedProps,
useSharedValue,
withRepeat,
withTiming,
} from 'react-native-reanimated';
import {
Circle,
FeColorMatrix,
FeColorMatrixProps,
Filter,
Image,
Svg,
} from 'react-native-svg';
const AnimatedFeColorMatrix = Animated.createAnimatedComponent(
FeColorMatrix as any,
) as React.FunctionComponent<AnimatedProps<FeColorMatrixProps>>;
const ReanimatedHueRotateExample = () => {
const hue = useSharedValue(0);
useEffect(() => {
hue.value = withRepeat(withTiming(360, {duration: 2000}), -1, true);
}, []);
const animatedProps = useAnimatedProps(() => {
return {values: [hue.value]};
});
return (
<Svg height="100" width="150">
<Filter id="filter">
<AnimatedFeColorMatrix type="hueRotate" animatedProps={animatedProps} />
</Filter>
<Image
href="https://cdn.pixabay.com/photo/2024/05/26/00/40/lizard-8787888_1280.jpg"
height="100"
width="150"
filter="url(#filter)"
/>
</Svg>
);
};
ReanimatedHueRotateExample.title = 'Reanimated Hue Rotate';
const icon = (
<Svg height="30" width="30" viewBox="0 0 20 20">
<Circle cx="10" cy="7.5" r="5" fill="blue" fillOpacity="0.5" />
<Circle cx="7.5" cy="12.5" r="5" fill="green" fillOpacity="0.5" />
<Circle cx="12.5" cy="12.5" r="5" fill="red" fillOpacity="0.5" />
</Svg>
);
const samples = [ReanimatedHueRotateExample];
export {icon, samples};

View File

@@ -0,0 +1,3 @@
import * as FeColorMatrix from './FeColorMatrix';
import * as ReanimatedFeColorMatrix from './ReanimatedFeColorMatrix';
export {FeColorMatrix, ReanimatedFeColorMatrix};

View File

@@ -0,0 +1,64 @@
import React from 'react';
import {StyleSheet, Text, TouchableHighlight, View} from 'react-native';
import {Circle, Svg} from 'react-native-svg';
import * as examples from './examples';
import {commonStyles} from '../../commonStyles';
const FiltersList = () => {
const [example, setExample] = React.useState<keyof typeof examples | null>(
null,
);
if (example) {
return (
<>
{examples[example].samples.map((Sample, i) => (
<View style={commonStyles.example} key={`sample-${i}`}>
<Text style={commonStyles.sampleTitle}>{Sample.title}</Text>
<Sample />
</View>
))}
</>
);
}
return (
<View style={styles.container}>
<Text style={commonStyles.welcome}>SVG Filters</Text>
{Object.keys(examples).map((element, i) => {
const name = element as keyof typeof examples;
return (
<TouchableHighlight
style={styles.link}
underlayColor="#ccc"
key={`example-${name}`}
onPress={() => setExample(name)}>
<View style={commonStyles.cell}>
{examples[name].icon}
<Text style={commonStyles.title}>{name}</Text>
</View>
</TouchableHighlight>
);
})}
</View>
);
};
FiltersList.title = '';
const styles = StyleSheet.create({
container: {width: '100%'},
link: {height: 40},
});
const icon = (
<Svg height="30" width="30" viewBox="0 0 20 20">
<Circle cx="10" cy="7.5" r="5" fill="blue" fillOpacity="0.5" />
<Circle cx="7.5" cy="12.5" r="5" fill="green" fillOpacity="0.5" />
<Circle cx="12.5" cy="12.5" r="5" fill="red" fillOpacity="0.5" />
</Svg>
);
const samples = [FiltersList];
export {icon, samples};