Ver código fonte

Feature: SelectSheet component added.

lfabl 1 mês atrás
pai
commit
4cb2966304

+ 2 - 1
example/app.json

@@ -20,7 +20,8 @@
                 "backgroundColor": "#222222",
                 "foregroundImage": "./assets/icon.png"
             },
-            "package": "ncoreuikitmobile.example"
+            "package": "ncoreuikitmobile.example",
+            "softwareKeyboardLayoutMode": "resize"
         },
         "web": {
             "favicon": "./assets/favicon.png"

+ 40 - 5
example/src/pages/home/index.tsx

@@ -23,11 +23,43 @@ import type {
 } from "@react-navigation/native-stack";
 import type RootStackParamList from "../../navigation/type";
 
-const X = [{
-    t: "x",
-    p: "y",
-    n: 90
-}];
+const X = [
+    {
+        t: "xdfç.öhfdşkhdfşlhkfdşlhkdfşklhjmdfkhl3",
+        p: "ydfguıy8ı*pğ34erdx",
+        n: 90
+    },
+    {
+        t: "3t4uh5ıe6ıo6eıo6eoke6ıoke6ker6kr4ok",
+        p: "y3w46pjbd57ykdr",
+        n: 91
+    },
+    {
+        t: "xr*-0k4wkh-5w40k0*wek95ujk9*",
+        p: "y467o5*3evghe5",
+        n: 92
+    },
+    {
+        t: "x-40*tyg*0w4kyh*w0ko45u*ko0w",
+        p: "y3şçwl78570*fg",
+        n: 93
+    },
+    {
+        t: "*wrüsjh*ü9kws4*h0okdfhdhdfurdkex",
+        p: "yt7ıtot7ot",
+        n: 94
+    },
+    {
+        t: "x2-0*4woy-*w4opyu-*23wou*0wık*0jk",
+        p: "yasfa33",
+        n: 95
+    },
+    {
+        t: "x vmönöpwoı6-pğfş -*235*çda",
+        p: "ydfasdfasf",
+        n: 96
+    }
+];
 
 const Home = () => {
     const {
@@ -52,11 +84,14 @@ const Home = () => {
             subTitle="Deneme Subtitle"
             hintText="Test deneme"
             isShowSubTitle={true}
+            isMultipleSelect={true}
+            maxChoice={-1}
             // isWorkWithRealtime={false}
             initialSelectedItems={[{
                 __title: X[0]?.t as string,
                 __key: `${X[0]?.t}-0`
             }]}
+            minChoice={1}
             isSearchable={true}
             title="Deneme Box"
             isRequired={true}

+ 1 - 1
src/components/bottomSheet/index.tsx

@@ -678,7 +678,7 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
             return null;
         }
 
-        return <RenderHeaderComponent/>;
+        return RenderHeaderComponent();
     };
 
     const renderBottom = () => {

+ 4 - 0
src/components/index.ts

@@ -46,3 +46,7 @@ export {
 export type {
     IBottomSheetRef
 } from "./bottomSheet/type";
+
+export {
+    default as SelectSheet
+} from "./selectSheet";

+ 85 - 191
src/components/selectBox/index.tsx

@@ -33,9 +33,6 @@ import type {
     IBottomSheetRef
 } from "../bottomSheet/type";
 import type ITextProps from "../text/type";
-import {
-    CheckIcon
-} from "lucide-react-native";
 import {
     BadgeQuestionMarkIcon,
     BadgeSuccessIcon,
@@ -47,10 +44,7 @@ import {
 import {
     uuid
 } from "../../utils";
-import BottomSheet from "../bottomSheet";
-import TextInput from "../textInput";
-import Loading from "../loading";
-import Button from "../button";
+import SelectSheet from "../selectSheet";
 import Text from "../text";
 
 const SelectBoxTypeIcon: Record<Exclude<SelectBoxType, "default">, NCoreUIKitIcon> = {
@@ -68,6 +62,7 @@ function SelectBox<T>({
     spreadBehaviour = "baseline",
     isShowHintTextIcon = false,
     isWorkWithRealtime = true,
+    isMultipleSelect = false,
     icon: IconComponentProp,
     hintTextContainerStyle,
     removeSelectValidation,
@@ -98,6 +93,8 @@ function SelectBox<T>({
     isOptional,
     renderItem,
     customKey,
+    maxChoice,
+    minChoice,
     iconStyle,
     onChange,
     hintText,
@@ -127,17 +124,8 @@ function SelectBox<T>({
     });
 
     const {
-        bottomSheetContentContainer: bottomSheetContentContainerDynamicStyle,
-        bottomSheetHeaderContainer: bottomSheetHeaderContainerDynamicStyle,
-        bottomSheetBottomContainer: bottomSheetBottomContainerDynamicStyle,
-        bottomSheetCancelButton: bottomSheetCancelButtonDynamicStyle,
-        bottomSheetHeaderTitle: bottomSheetHeaderTitleDynamicStyle,
-        bottomSheetOkButton: bottomSheetOkButtonDynamicStyle,
-        checkIconContainer: checkIconContainerDynamicStyle,
-        loadingContainer: loadingContainerDynamicStyle,
         contentContainer: contentContainerDynamicStyle,
         titleContainer: titleContainerDynamicStyle,
-        itemContainer: itemContainerDynamicStyle,
         hintTextIcon: hintTextIconDynamicStyle,
         contentText: contentTextDynamicStyle,
         cleanButton: cleanButtonDynamicStyle,
@@ -243,9 +231,9 @@ function SelectBox<T>({
 
                 setSearchedData(_searchedData);
             } else {
-                const _searchedData = prepareDatas(data.filter(dI => {
+                const _searchedData = data.filter(dI => {
                     return dI.__title.toLocaleLowerCase().includes(searchText.toLocaleLowerCase());
-                }) as Array<T & SelectedItem>, mainSelectedItems);
+                }) as Array<T & SelectedItem>;
 
                 setSearchedData(_searchedData);
             }
@@ -400,26 +388,67 @@ function SelectBox<T>({
         let _selectedItems = JSON.parse(JSON.stringify(mainSelectedItems));
 
         if(isAlreadySelected !== -1) {
-            if(removeSelectValidation) {
-                if(removeSelectValidation(selectedItem)) {
-                    _selectedItems = _selectedItems.filter((sItem: SelectedItem) => sItem.__key !== selectedItem.__key);
+            let isProcessPerm = false;
+
+            if(isMultipleSelect) {
+                if(minChoice) {
+                    if(_selectedItems.length > minChoice) {
+                        isProcessPerm = true;
+                    }
+                } else {
+                    isProcessPerm = true;
                 }
             } else {
-                _selectedItems = _selectedItems.filter((sItem: SelectedItem) => sItem.__key !== selectedItem.__key);
+                const _minChoice = minChoice === undefined ? 0 : minChoice;
+
+                if(_minChoice > 0) {
+                    // TODO: make toast;
+                } else {
+                    isProcessPerm = true;
+                }
+            }
+
+            if(isProcessPerm) {
+                if(removeSelectValidation) {
+                    if(removeSelectValidation(selectedItem)) {
+                        _selectedItems = _selectedItems.filter((sItem: SelectedItem) => sItem.__key !== selectedItem.__key);
+                    }
+                } else {
+                    _selectedItems = _selectedItems.filter((sItem: SelectedItem) => sItem.__key !== selectedItem.__key);
+                }
             }
         } else {
-            if(selectValidation) {
-                if(selectValidation(selectedItem)) {
+            let isProcessPerm = false;
+
+            if(isMultipleSelect) {
+                if(!maxChoice) {
+                    throw new Error("If you want to use the \"isMultipleSelect\" prop, you must be provide \"maxChoice\" prop.");
+                }
+
+                if(_selectedItems.length < maxChoice || maxChoice === -1) {
+                    isProcessPerm = true;
+                } else {
+                    // TODO: make toast;
+                }
+            } else {
+                _selectedItems = [];
+                isProcessPerm = true;
+            }
+
+            if(isProcessPerm) {
+                if(selectValidation) {
+                    if(selectValidation(selectedItem)) {
+                        _selectedItems.push({
+                            __title: selectedItem.__title,
+                            __key: selectedItem.__key
+                        });
+                    }
+                } else {
                     _selectedItems.push({
                         __title: selectedItem.__title,
                         __key: selectedItem.__key
                     });
                 }
-            } else {
-                _selectedItems.push({
-                    __title: selectedItem.__title,
-                    __key: selectedItem.__key
-                });
             }
         }
 
@@ -682,167 +711,6 @@ function SelectBox<T>({
         />;
     };
 
-    const renderBottomSheetContent = () => {
-        if(isLoading) {
-            return <View
-                style={[
-                    stylesheet.loadingContainer,
-                    loadingContainerDynamicStyle
-                ]}
-            >
-                {LoadingIconComponentProp ? <LoadingIconComponentProp/> : <Loading/>}
-            </View>;
-        }
-
-        return <View
-            style={[
-                bottomSheetContentContainerDynamicStyle
-            ]}
-        >
-            {
-                data.map((item: T & SelectedItem | SelectedItem, index: number) => {
-                    const isSelected = mainSelectedItems.findIndex((sI: SelectedItem) => {
-                        return sI.__key === item.__key;
-                    }) !== -1;
-
-                    if(renderItem) {
-                        return renderItem({
-                            key: keyExtractor(item as T & SelectedItem, index),
-                            onSelect: (sItem) => {
-                                selectObject(sItem);
-                            },
-                            selectedItems: mainSelectedItems,
-                            setIsLoading,
-                            isSelected,
-                            searchText,
-                            index,
-                            item,
-                            data
-                        });
-                    }
-
-                    return <TouchableOpacity
-                        key={keyExtractor(item as T & SelectedItem, index)}
-                        style={[
-                            stylesheet.itemContainer,
-                            itemContainerDynamicStyle
-                        ]}
-                        onPress={() => {
-                            selectObject(item);
-                        }}
-                    >
-                        <View
-                            style={[
-                                stylesheet.itemContentContainer
-                            ]}
-                        >
-                            {renderOptionIcon ? renderOptionIcon({
-                                item,
-                                index
-                            }) : null}
-                            <Text
-                                color={isSelected ? "emphasized" : undefined}
-                            >{item.__title}</Text>
-                        </View>
-                        <View
-                            style={[
-                                stylesheet.checkIconContainer,
-                                checkIconContainerDynamicStyle
-                            ]}
-                        >
-                            {isSelected ?
-                                <CheckIcon
-                                    color={colors.content.icon.emphasized}
-                                    size={24}
-                                /> : null}
-                        </View>
-                    </TouchableOpacity>;
-                })
-            }
-        </View>;
-    };
-
-    const renderBottomSheetHeader = () => {
-        return <View
-            style={[
-                stylesheet.bottomSheetHeaderContainer,
-                bottomSheetHeaderContainerDynamicStyle
-            ]}
-        >
-            <Text
-                variant="titleMediumSize"
-                style={[
-                    bottomSheetHeaderTitleDynamicStyle
-                ]}
-            >
-                {title}
-            </Text>
-            {isSearchable ? <TextInput
-                placeholder={localize("search")}
-                spreadBehaviour="stretch"
-                onChangeText={(text: string) => {
-                    setSearchText(text);
-                }}
-            /> : null}
-        </View>;
-    };
-
-    const renderBottomSheetBottom = () => {
-        if(isWorkWithRealtime) {
-            return null;
-        }
-
-        return <View
-            style={[
-                stylesheet.bottomSheetBottomContainer,
-                bottomSheetBottomContainerDynamicStyle
-            ]}
-        >
-            <Button
-                title={localize("cancel")}
-                spreadBehaviour="stretch"
-                variant="outline"
-                type="neutral"
-                onPress={() => {
-                    cancel();
-                }}
-                style={{
-                    ...bottomSheetCancelButtonDynamicStyle
-                }}
-            />
-            <Button
-                spreadBehaviour="stretch"
-                title={localize("ok")}
-                onPress={() => {
-                    ok();
-                }}
-                style={{
-                    ...bottomSheetOkButtonDynamicStyle
-                }}
-            />
-        </View>;
-    };
-
-    const renderBottomSheet = () => {
-        return <BottomSheet
-            customKey={selectBoxKey.current}
-            ref={bottomSheetRef}
-            isAutoHeight={true}
-            isActive={isActive}
-            renderBottom={() => {
-                return renderBottomSheetBottom();
-            }}
-            renderHeader={() => {
-                return renderBottomSheetHeader();
-            }}
-            onClose={() => {
-                setIsActive(false);
-            }}
-        >
-            {renderBottomSheetContent()}
-        </BottomSheet>;
-    };
-
     return <View
         style={[
             style,
@@ -876,7 +744,33 @@ function SelectBox<T>({
         </TouchableOpacity>
 
         {renderHintText()}
-        {renderBottomSheet()}
+
+        <SelectSheet
+            LoadingIconComponentProp={LoadingIconComponentProp}
+            isWorkWithRealtime={isWorkWithRealtime}
+            mainSelectedItems={mainSelectedItems}
+            isMultipleSelect={isMultipleSelect}
+            renderOptionIcon={renderOptionIcon}
+            selectBoxKey={selectBoxKey.current}
+            bottomSheetRef={bottomSheetRef}
+            setSearchText={setSearchText}
+            searchedData={searchedData}
+            isSearchable={isSearchable}
+            keyExtractor={keyExtractor}
+            selectObject={selectObject}
+            setIsLoading={setIsLoading}
+            setIsActive={setIsActive}
+            searchText={searchText}
+            renderItem={renderItem}
+            maxChoice={maxChoice}
+            minChoice={minChoice}
+            isLoading={isLoading}
+            isActive={isActive}
+            cancel={cancel}
+            title={title}
+            data={data}
+            ok={ok}
+        />
     </View>;
 };
 export default forwardRef(SelectBox) as unknown as SelectBoxComponent;

+ 1 - 62
src/components/selectBox/stylesheet.ts

@@ -93,7 +93,7 @@ export const getSelectBoxType = ({
 
     return currentType;
 };
-// TODO: PLACECHOLDER CODE ADD.
+
 const stylesheet = StyleSheet.create({
     container: {
         flexDirection: "column",
@@ -113,30 +113,6 @@ const stylesheet = StyleSheet.create({
     contentText: {
         minHeight: 20
     },
-    bottomSheetHeaderContainer: {
-        flexDirection: "column",
-        width: "100%"
-    },
-    bottomSheetBottomContainer: {
-        flexDirection: "row",
-        alignItems: "center"
-    },
-    itemContainer: {
-        flexDirection: "row",
-        alignItems: "center"
-    },
-    itemContentContainer: {
-        flex: 1
-    },
-    checkIconContainer: {
-        justifyContent: "center",
-        alignItems: "center"
-    },
-    loadingContainer: {
-        justifyContent: "center",
-        alignItems: "center",
-        flex: 1
-    },
     cleanButton: {
         justifyContent: "center",
         alignItems: "center",
@@ -185,7 +161,6 @@ const stylesheet = StyleSheet.create({
 
 export const useStyles = ({
     spreadBehaviour,
-    isSearchable,
     inlineSpaces,
     currentType,
     isDisabled,
@@ -216,38 +191,6 @@ export const useStyles = ({
         contentText: {
 
         } as Mutable<TextStyle>,
-        bottomSheetContentContainer: {
-            padding: spaces.spacingSm
-        } as Mutable<ViewStyle>,
-        bottomSheetHeaderContainer: {
-            padding: spaces.spacingSm
-        } as Mutable<ViewStyle>,
-        bottomSheetHeaderTitle: {
-
-        } as Mutable<ViewStyle>,
-        bottomSheetBottomContainer: {
-            marginTop: spaces.spacingSm,
-            padding: spaces.spacingSm
-        } as Mutable<ViewStyle>,
-        bottomSheetCancelButton: {
-            marginRight: spaces.spacingSm
-        } as Mutable<ViewStyle>,
-        bottomSheetOkButton: {
-            marginLeft: spaces.spacingSm
-        } as Mutable<ViewStyle>,
-        itemContainer: {
-            backgroundColor: colors.content.container.mid,
-            paddingHorizontal: spaces.spacingMd,
-            paddingVertical: spaces.spacingSm,
-            borderRadius: radiuses.form
-        } as Mutable<ViewStyle>,
-        checkIconContainer: {
-            height: 24 + spaces.spacingSm,
-            width: 24 + spaces.spacingSm
-        } as Mutable<ViewStyle>,
-        loadingContainer: {
-            padding: spaces.spacingMd
-        } as Mutable<ViewStyle>,
         cleanButton: {
             marginLeft: spaces.spacingSm
         } as Mutable<ViewStyle>,
@@ -280,10 +223,6 @@ export const useStyles = ({
         } as Mutable<ViewStyle>
     };
 
-    if(isSearchable) {
-        styles.bottomSheetHeaderTitle.marginBottom = spaces.spacingSm;
-    }
-
     if(isDisabled) {
         const disableStyleType = styleType === "default" ? "neutral" : styleType;
 

+ 3 - 0
src/components/selectBox/type.ts

@@ -99,6 +99,7 @@ interface ISelectBoxProps<T> {
         key: string;
     }) => ReactElement;
     isWorkWithRealtime?: boolean;
+    isMultipleSelect?: boolean;
     cleanIconStyle?: ViewStyle;
     rightIconStyle?: ViewStyle;
     rightIcon?: NCoreUIKitIcon;
@@ -118,6 +119,8 @@ interface ISelectBoxProps<T> {
     type?: SelectBoxType;
     onBlur?: () => void;
     customKey?: string;
+    maxChoice?: number;
+    minChoice?: number;
     hintText?: string;
     subTitle?: string;
     style?: ViewStyle;

+ 286 - 0
src/components/selectSheet/index.tsx

@@ -0,0 +1,286 @@
+import {
+    TouchableOpacity,
+    View
+} from "react-native";
+import {
+    type SelectedItem
+} from "./type";
+import type ISelectSheetProps from "./type";
+import stylesheet, {
+    useStyles
+} from "./stylesheet";
+import {
+    NCoreUIKitLocalize,
+    NCoreUIKitTheme
+} from "../../core/hooks";
+import {
+    CheckIcon
+} from "lucide-react-native";
+import BottomSheet from "../bottomSheet";
+import TextInput from "../textInput";
+import Loading from "../loading";
+import Button from "../button";
+import Text from "../text";
+
+function SelectSheet<T>({
+    LoadingIconComponentProp,
+    isWorkWithRealtime,
+    mainSelectedItems,
+    isMultipleSelect,
+    renderOptionIcon,
+    bottomSheetRef,
+    setSearchText,
+    selectBoxKey,
+    setIsLoading,
+    selectObject,
+    keyExtractor,
+    searchedData,
+    isSearchable,
+    setIsActive,
+    renderItem,
+    searchText,
+    maxChoice,
+    isLoading,
+    isActive,
+    cancel,
+    title,
+    data,
+    ok
+}: ISelectSheetProps<T>) {
+    const {
+        radiuses,
+        colors,
+        spaces
+    } = NCoreUIKitTheme.useContext();
+
+    const {
+        localize
+    } = NCoreUIKitLocalize.useContext();
+
+    const {
+        checkIconContainer: checkIconContainerDynamicStyle,
+        loadingContainer: loadingContainerDynamicStyle,
+        contentContainer: contentContainerDynamicStyle,
+        headerContainer: headerContainerDynamicStyle,
+        bottomContainer: bottomContainerDynamicStyle,
+        itemContainer: itemContainerDynamicStyle,
+        cancelButton: cancelButtonDynamicStyle,
+        headerTitle: headerTitleDynamicStyle,
+        okButton: okButtonDynamicStyle
+    } = useStyles({
+        isSearchable,
+        radiuses,
+        spaces,
+        colors
+    });
+
+    const renderBottomSheetContent = () => {
+        if(isLoading) {
+            return <View
+                style={[
+                    stylesheet.loadingContainer,
+                    loadingContainerDynamicStyle
+                ]}
+            >
+                {LoadingIconComponentProp ? <LoadingIconComponentProp/> : <Loading/>}
+            </View>;
+        }
+
+        const currentData = searchText && searchText.length ? searchedData : data;
+
+        return <View
+            style={[
+                contentContainerDynamicStyle
+            ]}
+        >
+            {
+                currentData.map((item: T & SelectedItem | SelectedItem, index: number) => {
+                    const isSelected = mainSelectedItems.findIndex((sI: SelectedItem) => {
+                        return sI.__key === item.__key;
+                    }) !== -1;
+
+                    if(renderItem) {
+                        return renderItem({
+                            key: keyExtractor(item as T & SelectedItem, index),
+                            onSelect: (sItem) => {
+                                selectObject(sItem);
+                            },
+                            selectedItems: mainSelectedItems,
+                            setIsLoading,
+                            isSelected,
+                            searchText,
+                            index,
+                            item,
+                            data
+                        });
+                    }
+
+                    let isDisabled = false;
+
+                    if(isMultipleSelect && !isSelected && maxChoice !== -1) {
+                        if(!maxChoice) {
+                            throw new Error("If you want to use the \"isMultipleSelect\" prop, you must be provide \"maxChoice\" prop.");
+                        }
+
+                        if(mainSelectedItems.length >= maxChoice) {
+                            isDisabled = true;
+                        }
+                    }
+
+                    return <TouchableOpacity
+                        key={keyExtractor(item as T & SelectedItem, index)}
+                        disabled={isDisabled}
+                        style={[
+                            stylesheet.itemContainer,
+                            itemContainerDynamicStyle,
+                            {
+                                marginTop: index === 0 ? 0 : spaces.spacingSm,
+                                position: "relative"
+                            }
+                        ]}
+                        onPress={() => {
+                            if(isDisabled) {
+                                return;
+                            }
+
+                            selectObject(item);
+                        }}
+                    >
+                        <View
+                            style={[
+                                stylesheet.itemContentContainer,
+                                {
+                                    zIndex: 99999
+                                }
+                            ]}
+                        >
+                            {renderOptionIcon ? renderOptionIcon({
+                                item,
+                                index
+                            }) : null}
+                            <Text
+                                color={isSelected ? "emphasized" : undefined}
+                            >{item.__title}</Text>
+                        </View>
+                        <View
+                            style={[
+                                stylesheet.checkIconContainer,
+                                checkIconContainerDynamicStyle
+                            ]}
+                        >
+                            {isSelected ?
+                                <CheckIcon
+                                    color={colors.content.icon.emphasized}
+                                    size={24}
+                                /> : null}
+                        </View>
+                        {isDisabled ? <View
+                            style={[
+                                {
+                                    backgroundColor: colors.system.disabled.content,
+                                    borderRadius: radiuses.actions,
+                                    position: "absolute",
+                                    zIndex: 99998,
+                                    bottom: 0,
+                                    right: 0,
+                                    left: 0,
+                                    top: 0
+                                }
+                            ]}
+                        /> : null}
+                    </TouchableOpacity>;
+                })
+            }
+        </View>;
+    };
+
+    const renderBottomSheetSearchInput = () => {
+        if(!isSearchable) {
+            return null;
+        }
+
+        return <TextInput
+            key={`${selectBoxKey}-searchInput`}
+            placeholder={localize("search")}
+            spreadBehaviour="stretch"
+            onChangeText={(text: string) => {
+                setSearchText(text);
+            }}
+        />;
+    };
+
+    const renderBottomSheetHeader = () => {
+        return <View
+            style={[
+                stylesheet.headerContainer,
+                headerContainerDynamicStyle
+            ]}
+        >
+            <Text
+                variant="titleMediumSize"
+                style={[
+                    headerTitleDynamicStyle
+                ]}
+            >
+                {title}
+            </Text>
+            {renderBottomSheetSearchInput()}
+        </View>;
+    };
+
+    const renderBottomSheetBottom = () => {
+        if(isWorkWithRealtime) {
+            return null;
+        }
+
+        return <View
+            style={[
+                stylesheet.bottomContainer,
+                bottomContainerDynamicStyle
+            ]}
+        >
+            <Button
+                title={localize("cancel")}
+                spreadBehaviour="stretch"
+                variant="outline"
+                type="neutral"
+                onPress={() => {
+                    cancel();
+                }}
+                style={{
+                    ...cancelButtonDynamicStyle
+                }}
+            />
+            <Button
+                spreadBehaviour="stretch"
+                title={localize("ok")}
+                onPress={() => {
+                    ok();
+                }}
+                style={{
+                    ...okButtonDynamicStyle
+                }}
+            />
+        </View>;
+    };
+
+    return <BottomSheet
+        key={`${selectBoxKey}-bottomsheet`}
+        customKey={selectBoxKey}
+        ref={bottomSheetRef}
+        isAutoHeight={true}
+        isActive={isActive}
+        renderBottom={() => {
+            return renderBottomSheetBottom();
+        }}
+        renderHeader={() => {
+            return renderBottomSheetHeader();
+        }}
+        onClose={() => {
+            setIsActive(false);
+        }}
+    >
+        {renderBottomSheetContent()}
+    </BottomSheet>;
+};
+export default SelectSheet;

+ 86 - 0
src/components/selectSheet/stylesheet.ts

@@ -0,0 +1,86 @@
+import {
+    type ViewStyle,
+    StyleSheet
+} from "react-native";
+import {
+    type SelectSheetDynamicStyleType
+} from "./type";
+import type {
+    Mutable
+} from "../../types";
+
+const stylesheet = StyleSheet.create({
+    headerContainer: {
+        flexDirection: "column",
+        width: "100%"
+    },
+    bottomContainer: {
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    loadingContainer: {
+        justifyContent: "center",
+        alignItems: "center",
+        flex: 1
+    },
+    itemContainer: {
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    itemContentContainer: {
+        flex: 1
+    },
+    checkIconContainer: {
+        justifyContent: "center",
+        alignItems: "center"
+    }
+});
+
+export const useStyles = ({
+    isSearchable,
+    radiuses,
+    spaces,
+    colors
+}: SelectSheetDynamicStyleType) => {
+    const styles = {
+        contentContainer: {
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        headerContainer: {
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        headerTitle: {
+
+        } as Mutable<ViewStyle>,
+        bottomContainer: {
+            marginTop: spaces.spacingSm,
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        cancelButton: {
+            marginRight: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        okButton: {
+            marginLeft: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        itemContainer: {
+            backgroundColor: colors.content.container.mid,
+            paddingHorizontal: spaces.spacingMd,
+            paddingVertical: spaces.spacingSm,
+            borderRadius: radiuses.form
+        } as Mutable<ViewStyle>,
+        checkIconContainer: {
+            height: 24 + spaces.spacingSm,
+            width: 24 + spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        loadingContainer: {
+            padding: spaces.spacingMd
+        } as Mutable<ViewStyle>
+    };
+
+    if(isSearchable) {
+        styles.headerTitle.marginBottom = spaces.spacingSm;
+    }
+
+    return styles;
+};
+export default stylesheet;

+ 67 - 0
src/components/selectSheet/type.ts

@@ -0,0 +1,67 @@
+import type {
+    SetStateAction,
+    ReactElement,
+    Dispatch,
+    Ref
+} from "react";
+import {
+    type NCoreUIKitIcon
+} from "../../types";
+import type {
+    IBottomSheetRef
+} from "../bottomSheet/type";
+
+export type SelectSheetDynamicStyleType = {
+    radiuses: NCoreUIKit.ActivePalette["radiuses"];
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+    isSearchable?: boolean;
+};
+
+export type SelectedItem = {
+    __title: string;
+    __key: string;
+};
+
+interface ISelectSheetProps<T> {
+    selectObject: (selectedItem: T & SelectedItem | SelectedItem) => void;
+    keyExtractor: (item: T & SelectedItem, index: number) => string;
+    searchedData: Array<SelectedItem | (T & SelectedItem)>;
+    renderItem?: (props: {
+        onSelect: (selectedItem: SelectedItem) => void;
+        data: Array<T & SelectedItem | SelectedItem>;
+        setIsLoading: (state: boolean) => void;
+        selectedItems: Array<SelectedItem>;
+        isSelected: boolean;
+        searchText: string;
+        item: SelectedItem;
+        index: number;
+        key: string;
+    }) => ReactElement;
+    setSearchText: Dispatch<SetStateAction<string>>;
+    setIsLoading: Dispatch<SetStateAction<boolean>>;
+    setIsActive: Dispatch<SetStateAction<boolean>>;
+    data: Array<SelectedItem | (T & SelectedItem)>;
+    bottomSheetRef: Ref<IBottomSheetRef> | null;
+    LoadingIconComponentProp?: NCoreUIKitIcon;
+    renderOptionIcon?: (props: {
+        item: T & SelectedItem | SelectedItem;
+        index: number;
+    }) => ReactElement;
+    mainSelectedItems: Array<SelectedItem>;
+    isWorkWithRealtime: boolean;
+    isMultipleSelect?: boolean;
+    isSearchable?: boolean;
+    selectBoxKey: string;
+    maxChoice?: number;
+    minChoice?: number;
+    searchText: string;
+    isLoading: boolean;
+    cancel: () => void;
+    isActive: boolean;
+    ok: () => void;
+    title?: string;
+};
+export type {
+    ISelectSheetProps as default
+};

+ 1 - 0
src/index.tsx

@@ -12,6 +12,7 @@ export {
 export {
     PageContainer,
     BottomSheet,
+    SelectSheet,
     TextInput,
     SelectBox,
     Loading,