الفرق بين المراجعتين لصفحة: «ReactNative/stylesheet»
رقية-بورية (نقاش | مساهمات) أنشأ الصفحة ب' = StyleSheet = تُعدّ StyleSheet تجريدًا مشابهًا لتنسيقات CSS. == مثال == <syntaxhighlight lang="javascript"> import React from "react";...' |
جميل-بيلوني (نقاش | مساهمات) طلا ملخص تعديل |
||
(6 مراجعات متوسطة بواسطة مستخدمين اثنين آخرين غير معروضة) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE:الواجهة StyleSheet في React Native}}</noinclude> | |||
تُعَدّ الواجهة <code>StyleSheet</code> تجريدًا مشابهًا لتنسيقات [[CSS]]، وكمثالٍ عنها ما يلي ([https://snack.expo.dev/@hsoubwiki/stylesheet تجربة حية]):<syntaxhighlight lang="javascript"> | |||
<syntaxhighlight lang="javascript"> | |||
import React from "react"; | import React from "react"; | ||
import { StyleSheet, Text, View } from "react-native"; | import { StyleSheet, Text, View } from "react-native"; | ||
const App = () => ( | const App = () => ( | ||
<View style={styles.container}> | |||
<Text style={styles.title}>React Native</Text> | |||
</View> | |||
); | ); | ||
const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||
container: { | |||
flex: 1, | |||
padding: 24, | |||
backgroundColor: "#eaeaea" | |||
}, | }, | ||
title: { | |||
marginTop: 16, | |||
paddingVertical: 8, | |||
borderWidth: 4, | |||
borderColor: "#20232a", | |||
borderRadius: 6, | |||
backgroundColor: "#61dafb", | |||
color: "#20232a", | |||
textAlign: "center", | |||
fontSize: 30, | |||
fontWeight: "bold" | |||
} | } | ||
}); | }); | ||
export default App; | export default App; | ||
</syntaxhighlight>نصائح لتحسين جودة الشّفرة البرمجيّة: | </syntaxhighlight>نصائح لتحسين جودة الشّفرة البرمجيّة: | ||
* إزالة التّنسيقات (styles) من تابع العرض (render) تجعل الشّفرة البرمجيّة سهلة الفهم. | * إزالة التّنسيقات (styles) من تابع العرض (render) تجعل الشّفرة البرمجيّة سهلة الفهم. | ||
* تعدّ تسمية التّنسيقات طريقةً جيّدةً لإعطاء معنىً للمكوّنات منخفضة المستوى (low level) في تابع الإظهار. | * تعدّ تسمية التّنسيقات طريقةً جيّدةً لإعطاء معنىً للمكوّنات منخفضة المستوى (low level)، في تابع الإظهار. | ||
__toc__ | |||
== | == التوابع == | ||
=== <code>compose()</code> === | === <code>compose()</code> === | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
static compose(style1: object, style2: object): object | array<object> | static compose(style1: object, style2: object): object | array<object> | ||
</syntaxhighlight>يُستخدم لدمج تنسيقين، حيث يقوم <code>style2</code> بإلغاء أيّ تنسيقاتٍ موجودةٍ في <code>style1</code> | </syntaxhighlight>يُستخدم هذا التابع لدمج تنسيقين، حيث يقوم <code>style2</code> بإلغاء أيّ تنسيقاتٍ موجودةٍ في <code>style1</code>؛ أمّا في حال كان أحد التنسيقين غير صالح فسيعاد الآخر دون أي تخصيص في المصفوفة، مع حفظ التّخصيصات، والمحافظة على المساواة المرجعيّة (reference equality) من أجل فحوصات PureComponent. | ||
إليك المثال التالي ([https://snack.expo.dev/@hsoubwiki/stylesheet-compose تجربة حية]): <syntaxhighlight lang="javascript"> | |||
import React from 'react'; | import React from 'react'; | ||
import { StyleSheet, Text, View } from 'react-native'; | import { StyleSheet, Text, View } from 'react-native'; | ||
const App = () => ( | const App = () => ( | ||
<View style={container}> | |||
<Text style={text}>React Native</Text> | |||
</View> | |||
); | ); | ||
const page = StyleSheet.create({ | const page = StyleSheet.create({ | ||
container: { | |||
flex: 1, | |||
padding: 24, | |||
backgroundColor: '#fff', | |||
}, | }, | ||
text: { | |||
fontSize: 30, | |||
color: '#000' | |||
}, | }, | ||
}); | }); | ||
const lists = StyleSheet.create({ | const lists = StyleSheet.create({ | ||
listContainer: { | |||
flex: 1, | |||
backgroundColor: '#61dafb', | |||
}, | }, | ||
listItem: { | |||
fontStyle: 'italic', | |||
fontWeight: 'bold' | |||
}, | }, | ||
}); | }); | ||
const container = StyleSheet.compose(page.container, lists.listContainer); | const container = StyleSheet.compose(page.container, lists.listContainer); | ||
const text = StyleSheet.compose(page.text, lists.listItem); | const text = StyleSheet.compose(page.text, lists.listItem); | ||
export default App; | export default App; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
سطر 87: | سطر 85: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
static create(obj: object): object | static create(obj: object): object | ||
</syntaxhighlight>يُستخدم لإنشاء مرجع تنسيق (StyleSheet) من الكائن المعطى. | </syntaxhighlight>يُستخدم هذا التابع لإنشاء مرجع تنسيق (StyleSheet) من الكائن المعطى. | ||
=== <code>flatten()</code> === | === <code>flatten()</code> === | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
static flatten(style: array<object>): object | static flatten(style: array<object>): object | ||
</syntaxhighlight>يُحوّل مصفوفة من كائنات التّنسيق إلى كائن تنسيقٍ واحدٍ، كما يمكن | </syntaxhighlight>يُحوّل مصفوفة من كائنات التّنسيق إلى كائن تنسيقٍ واحدٍ، كما يمكن استخدامه للبحث عن المعرِّفات (IDs) المُعادة من <code>StyleSheet.register</code>. | ||
'''ملاحظة:''' يجب توخي الحذر، لأنّ إساءة استخدام هذا التّابع سيؤثّر سلبًا على التّحسينات، فالمعرِّفات (IDs) تمكِّن التّحسينات من خلال الجسر، والذاكرة بشكلٍ عامٍّن لذا فالإشارة إلى كائنات التّنسيق بشكلٍ مباشرٍ ستؤدي إلى الحرمان من هذه التّحسينات. | |||
إليك المثال التالي ([https://snack.expo.dev/@hsoubwiki/stylesheet-flatten تجربة حية]):<syntaxhighlight lang="javascript"> | |||
import React from "react"; | import React from "react"; | ||
import { StyleSheet, Text, View } from "react-native"; | import { StyleSheet, Text, View } from "react-native"; | ||
const App = () => ( | const App = () => ( | ||
<View style={page.container}> | |||
<Text style={flattenStyle}>React Native</Text> | |||
<Text>Flatten Style</Text> | |||
<Text style={page.code}> | |||
{JSON.stringify(flattenStyle, null, 2)} | |||
</Text> | |||
</View> | |||
); | ); | ||
const page = StyleSheet.create({ | const page = StyleSheet.create({ | ||
container: { | |||
flex: 1, | |||
padding: 24, | |||
alignItems: "center" | |||
}, | }, | ||
text: { | |||
color: "#000", | |||
fontSize: 14, | |||
fontWeight: "bold" | |||
}, | }, | ||
code: { | |||
marginTop: 12, | |||
padding: 12, | |||
borderRadius: 8, | |||
color: "#666", | |||
backgroundColor: "#eaeaea" | |||
} | } | ||
}); | }); | ||
const typography = StyleSheet.create({ | const typography = StyleSheet.create({ | ||
header: { | |||
color: "#61dafb", | |||
fontSize: 30, | |||
marginBottom: 36 | |||
} | } | ||
}); | }); | ||
const flattenStyle = StyleSheet.flatten([ | const flattenStyle = StyleSheet.flatten([ | ||
page.text, | |||
typography.header | |||
]); | ]); | ||
export default App; | export default App; | ||
</syntaxhighlight>يستخدم هذا التابع <code>StyleSheetRegistry.getStyleByID(style | </syntaxhighlight>يستخدم هذا التابع <code>(StyleSheetRegistry.getStyleByID(style</code> بشكلً ضمنيٍّ للحصول على كائنات التّنسيق الممثّلة بالمعرِّفات (IDs)، من مصفوفة كائنات التنسيق (الناتجة عن <code>StyleSheet.create()</code>)، وتجميعها ضمن كائنٍ وحيدٍ ثمّ إعادته. وهذا يشرح أيضًا الاستخدام البديل لهذا التابع. | ||
=== <code>setStyleAttributePreprocessor()</code> === | === <code>setStyleAttributePreprocessor()</code> === | ||
<blockquote>'''تحذير: | <blockquote>'''تحذير:''' تجريبيًّا قد تحدث التغيّرات العاجلة كثيرًا دون الإعلان عنها بشكلٍ موثوقٍ، وقد يحذف كلّ شيءٍ، من يعلم؟ لذا استخدم هذه الدّالة على مسؤوليّتك الشخصيّة.</blockquote><syntaxhighlight lang="javascript"> | ||
static setStyleAttributePreprocessor(property: string, process: (propValue: any) => any) | static setStyleAttributePreprocessor(property: string, process: (propValue: any) => any) | ||
</syntaxhighlight>يستخدم للمعالجة الأوليّة (pre-process) لقيم خاصيّات التّنسيق، ويستخدَم داخليًّا لمعالجة | </syntaxhighlight>يستخدم للمعالجة الأوليّة (pre-process) لقيم خاصيّات التّنسيق، ويستخدَم داخليًّا لمعالجة اللّون، وتحويل القيم، ويفضَّل عدم استخدمه إلّا إذا كنت تعلم ما الذي تفعله، وقد استنفدت الخيارات الأخرى. | ||
== | == الخاصيات (props) == | ||
=== <code>absoluteFill</code> === | === <code>absoluteFill</code> === | ||
يعدّ إنشاء تراكباتٍ (overlays) ذات موقع مطلقٍ، وتوضُّع صفري، هو النّموذج الشائع (<code>position: 'absolute', left: 0, right: 0, top: 0, bottom: 0</code>)، لذا يستخدم <code>absoluteFill</code> للملائمة، وتقليل تضاعف هذه التّنسيقات المتكرّرة، كما يستخدم لإنشاء مدخل مخصّصٍ ضمن التنسيقات (StyleSheet) كما في المثال التالي ([https://snack.expo.dev/@hsoubwiki/stylesheet-absolutefill تجربة حية]): <syntaxhighlight lang="javascript"> | |||
import React from 'react'; | import React from 'react'; | ||
import { StyleSheet, Text, View } from 'react-native'; | import { StyleSheet, Text, View } from 'react-native'; | ||
const App = () => ( | const App = () => ( | ||
<View style={styles.container}> | |||
<View style={styles.box1}> | |||
<Text style={styles.text}>1</Text> | |||
</View> | |||
<View style={styles.box2}> | |||
<Text style={styles.text}>2</Text> | |||
</View> | |||
<View style={styles.box3}> | |||
<Text style={styles.text}>3</Text> | |||
</View> | |||
</View> | |||
); | ); | ||
const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||
container: { | |||
flex: 1 | |||
}, | }, | ||
box1: { | |||
position: 'absolute', | |||
top: 40, | |||
left: 40, | |||
width: 100, | |||
height: 100, | |||
backgroundColor: 'red' | |||
}, | }, | ||
box2: { | |||
...StyleSheet.absoluteFill, | |||
width: 100, | |||
height: 100, | |||
backgroundColor: 'blue' | |||
}, | }, | ||
box3: { | |||
position: 'absolute', | |||
top: 120, | |||
left: 120, | |||
width: 100, | |||
height: 100, | |||
backgroundColor: 'green' | |||
}, | }, | ||
text: { | |||
color: '#FFF', | |||
fontSize: 80 | |||
} | } | ||
}); | }); | ||
export default App; | export default App; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== <code>absoluteFillObject</code> === | === <code>absoluteFillObject</code> === | ||
<code>absoluteFill</code> نفسها مع بعض التّعديلات، | هي <code>absoluteFill</code> نفسها مع بعض التّعديلات، وتستخدَم لإنشاء مدخل مخصّصٍ ضمن التّنسيقات (StyleSheet) كما في المثال التالي ([https://snack.expo.dev/@hsoubwiki/stylesheet-absolutefillobject تجربة حية]):<syntaxhighlight lang="javascript"> | ||
import React from 'react'; | import React from 'react'; | ||
import { StyleSheet, Text, View } from 'react-native'; | import { StyleSheet, Text, View } from 'react-native'; | ||
سطر 258: | سطر 260: | ||
export default App; | export default App; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== الفرق بين <code>absoluteFill</code> و<code>absoluteFillObject</code>==== | |||
حاليًا لا يوجد فرق في الاستخدام بين <code>absoluteFill</code>، و<code>absoluteFillObject</code>. | |||
=== <code>hairlineWidth</code> === | === <code>hairlineWidth</code> === | ||
تعرّف على أنها عَرْض الخطّ الرّفيع على المنصّة، ويمكن استخدامها كسماكة | تعرّف هذه الخاصية على أنها عَرْض الخطّ الرّفيع على المنصّة، ويمكن استخدامها كسماكة الحوافّ، أو الفاصل بين عنصرين. إليك المثال التالي ([https://snack.expo.dev/@hsoubwiki/stylesheet-hairlinewidth تجربة حية]): <syntaxhighlight lang="javascript"> | ||
import React from "react"; | import React from "react"; | ||
import { StyleSheet, Text, View } from "react-native"; | import { StyleSheet, Text, View } from "react-native"; | ||
سطر 284: | سطر 289: | ||
export default App; | export default App; | ||
</syntaxhighlight>سيكون هذا الثابت دائمًا تقريبًا لعدد البكسلات (لذا سيكون الخط المعرَّف به متعرّجًا)، وسيحاول مطابقة العرض المعياريّ للخطّ الرفيع على المنصّة التحتية (underlying platform). يجب ألا نعتمد على ثبات | </syntaxhighlight>سيكون هذا الثابت دائمًا تقريبًا لعدد البكسلات (لذا سيكون الخط المعرَّف به متعرّجًا)، وسيحاول مطابقة العرض المعياريّ للخطّ الرفيع على المنصّة التحتية (underlying platform). يجب ألا نعتمد على ثبات حجمه، إذ يمكن حساب قيمته بشكلٍ مختلفٍ باختلاف المنصّة، وكثافة الشّاشة. كما قد لا يظهر الخط الذي بعرض hairline على المحاكي، إذا كان هذا المحاكي مصغّرّا. | ||
== مصادر == | == مصادر == | ||
* [https://facebook.github.io/react-native/docs/stylesheet صفحة StyleSheet في توثيق React Native الرسميّ] | * [https://facebook.github.io/react-native/docs/stylesheet صفحة StyleSheet في توثيق React Native الرسميّ] | ||
[[تصنيف:ReactNative]] | |||
[[تصنيف:React Native API]] |
المراجعة الحالية بتاريخ 14:13، 9 أكتوبر 2021
تُعَدّ الواجهة StyleSheet
تجريدًا مشابهًا لتنسيقات CSS، وكمثالٍ عنها ما يلي (تجربة حية):
import React from "react";
import { StyleSheet, Text, View } from "react-native";
const App = () => (
<View style={styles.container}>
<Text style={styles.title}>React Native</Text>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: "#eaeaea"
},
title: {
marginTop: 16,
paddingVertical: 8,
borderWidth: 4,
borderColor: "#20232a",
borderRadius: 6,
backgroundColor: "#61dafb",
color: "#20232a",
textAlign: "center",
fontSize: 30,
fontWeight: "bold"
}
});
export default App;
نصائح لتحسين جودة الشّفرة البرمجيّة:
- إزالة التّنسيقات (styles) من تابع العرض (render) تجعل الشّفرة البرمجيّة سهلة الفهم.
- تعدّ تسمية التّنسيقات طريقةً جيّدةً لإعطاء معنىً للمكوّنات منخفضة المستوى (low level)، في تابع الإظهار.
التوابع
compose()
static compose(style1: object, style2: object): object | array<object>
يُستخدم هذا التابع لدمج تنسيقين، حيث يقوم style2
بإلغاء أيّ تنسيقاتٍ موجودةٍ في style1
؛ أمّا في حال كان أحد التنسيقين غير صالح فسيعاد الآخر دون أي تخصيص في المصفوفة، مع حفظ التّخصيصات، والمحافظة على المساواة المرجعيّة (reference equality) من أجل فحوصات PureComponent.
إليك المثال التالي (تجربة حية):
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const App = () => (
<View style={container}>
<Text style={text}>React Native</Text>
</View>
);
const page = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: '#fff',
},
text: {
fontSize: 30,
color: '#000'
},
});
const lists = StyleSheet.create({
listContainer: {
flex: 1,
backgroundColor: '#61dafb',
},
listItem: {
fontStyle: 'italic',
fontWeight: 'bold'
},
});
const container = StyleSheet.compose(page.container, lists.listContainer);
const text = StyleSheet.compose(page.text, lists.listItem);
export default App;
create()
static create(obj: object): object
يُستخدم هذا التابع لإنشاء مرجع تنسيق (StyleSheet) من الكائن المعطى.
flatten()
static flatten(style: array<object>): object
يُحوّل مصفوفة من كائنات التّنسيق إلى كائن تنسيقٍ واحدٍ، كما يمكن استخدامه للبحث عن المعرِّفات (IDs) المُعادة من StyleSheet.register
.
ملاحظة: يجب توخي الحذر، لأنّ إساءة استخدام هذا التّابع سيؤثّر سلبًا على التّحسينات، فالمعرِّفات (IDs) تمكِّن التّحسينات من خلال الجسر، والذاكرة بشكلٍ عامٍّن لذا فالإشارة إلى كائنات التّنسيق بشكلٍ مباشرٍ ستؤدي إلى الحرمان من هذه التّحسينات.
إليك المثال التالي (تجربة حية):
import React from "react";
import { StyleSheet, Text, View } from "react-native";
const App = () => (
<View style={page.container}>
<Text style={flattenStyle}>React Native</Text>
<Text>Flatten Style</Text>
<Text style={page.code}>
{JSON.stringify(flattenStyle, null, 2)}
</Text>
</View>
);
const page = StyleSheet.create({
container: {
flex: 1,
padding: 24,
alignItems: "center"
},
text: {
color: "#000",
fontSize: 14,
fontWeight: "bold"
},
code: {
marginTop: 12,
padding: 12,
borderRadius: 8,
color: "#666",
backgroundColor: "#eaeaea"
}
});
const typography = StyleSheet.create({
header: {
color: "#61dafb",
fontSize: 30,
marginBottom: 36
}
});
const flattenStyle = StyleSheet.flatten([
page.text,
typography.header
]);
export default App;
يستخدم هذا التابع (StyleSheetRegistry.getStyleByID(style
بشكلً ضمنيٍّ للحصول على كائنات التّنسيق الممثّلة بالمعرِّفات (IDs)، من مصفوفة كائنات التنسيق (الناتجة عن StyleSheet.create()
)، وتجميعها ضمن كائنٍ وحيدٍ ثمّ إعادته. وهذا يشرح أيضًا الاستخدام البديل لهذا التابع.
setStyleAttributePreprocessor()
تحذير: تجريبيًّا قد تحدث التغيّرات العاجلة كثيرًا دون الإعلان عنها بشكلٍ موثوقٍ، وقد يحذف كلّ شيءٍ، من يعلم؟ لذا استخدم هذه الدّالة على مسؤوليّتك الشخصيّة.
static setStyleAttributePreprocessor(property: string, process: (propValue: any) => any)
يستخدم للمعالجة الأوليّة (pre-process) لقيم خاصيّات التّنسيق، ويستخدَم داخليًّا لمعالجة اللّون، وتحويل القيم، ويفضَّل عدم استخدمه إلّا إذا كنت تعلم ما الذي تفعله، وقد استنفدت الخيارات الأخرى.
الخاصيات (props)
absoluteFill
يعدّ إنشاء تراكباتٍ (overlays) ذات موقع مطلقٍ، وتوضُّع صفري، هو النّموذج الشائع (position: 'absolute', left: 0, right: 0, top: 0, bottom: 0
)، لذا يستخدم absoluteFill
للملائمة، وتقليل تضاعف هذه التّنسيقات المتكرّرة، كما يستخدم لإنشاء مدخل مخصّصٍ ضمن التنسيقات (StyleSheet) كما في المثال التالي (تجربة حية):
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const App = () => (
<View style={styles.container}>
<View style={styles.box1}>
<Text style={styles.text}>1</Text>
</View>
<View style={styles.box2}>
<Text style={styles.text}>2</Text>
</View>
<View style={styles.box3}>
<Text style={styles.text}>3</Text>
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1
},
box1: {
position: 'absolute',
top: 40,
left: 40,
width: 100,
height: 100,
backgroundColor: 'red'
},
box2: {
...StyleSheet.absoluteFill,
width: 100,
height: 100,
backgroundColor: 'blue'
},
box3: {
position: 'absolute',
top: 120,
left: 120,
width: 100,
height: 100,
backgroundColor: 'green'
},
text: {
color: '#FFF',
fontSize: 80
}
});
export default App;
absoluteFillObject
هي absoluteFill
نفسها مع بعض التّعديلات، وتستخدَم لإنشاء مدخل مخصّصٍ ضمن التّنسيقات (StyleSheet) كما في المثال التالي (تجربة حية):
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const App = () => (
<View style={styles.container}>
<View style={styles.box1}>
<Text style={styles.text}>1</Text>
</View>
<View style={styles.box2}>
<Text style={styles.text}>2</Text>
</View>
<View style={styles.box3}>
<Text style={styles.text}>3</Text>
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1
},
box1: {
position: 'absolute',
top: 40,
left: 40,
width: 100,
height: 100,
backgroundColor: 'red'
},
box2: {
...StyleSheet.absoluteFill,
top: 120,
left: 50,
width: 100,
height: 100,
backgroundColor: 'blue'
},
box3: {
...StyleSheet.absoluteFillObject,
top: 120,
left: 120,
width: 100,
height: 100,
backgroundColor: 'green'
},
text: {
color: '#FFF',
fontSize: 80
}
});
export default App;
الفرق بين absoluteFill
وabsoluteFillObject
حاليًا لا يوجد فرق في الاستخدام بين absoluteFill
، وabsoluteFillObject
.
hairlineWidth
تعرّف هذه الخاصية على أنها عَرْض الخطّ الرّفيع على المنصّة، ويمكن استخدامها كسماكة الحوافّ، أو الفاصل بين عنصرين. إليك المثال التالي (تجربة حية):
import React from "react";
import { StyleSheet, Text, View } from "react-native";
const App = () => (
<View style={styles.container}>
<Text style={styles.row}>React</Text>
<Text style={styles.row}>Native</Text>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24
},
row: {
padding: 4,
borderBottomColor: "red",
borderBottomWidth: StyleSheet.hairlineWidth
}
});
export default App;
سيكون هذا الثابت دائمًا تقريبًا لعدد البكسلات (لذا سيكون الخط المعرَّف به متعرّجًا)، وسيحاول مطابقة العرض المعياريّ للخطّ الرفيع على المنصّة التحتية (underlying platform). يجب ألا نعتمد على ثبات حجمه، إذ يمكن حساب قيمته بشكلٍ مختلفٍ باختلاف المنصّة، وكثافة الشّاشة. كما قد لا يظهر الخط الذي بعرض hairline على المحاكي، إذا كان هذا المحاكي مصغّرّا.