๋ฐฐ๋„ˆ/FlmBanner

๐ŸŒ ์Šคํฌ๋ฆฐ์ƒท/ScreenShot
๊ตฌ๊ธ€๋“œ๋ผ์ด๋ธŒ/Google Drive :
https://drive.google.com/drive/folders/1hhhIYmU8kSGOUTN9wd7H9igSppl6AE71?usp=drive_link

1.react-native-freelifemakers-ui NPM ๋ชจ๋“ˆ ์„ค์น˜ํ•˜๊ธฐ / react-native-freelifemakers-ui NPM Module Install

# npm install react-native-freelifemakers-ui

2.์˜์กด์„ฑ ๋ชจ๋“ˆ ์„ค์น˜ / peerModule install

— FlmBottomSheet๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฐ˜๋“œ์‹œ ์„ค์น˜ํ•ด์•ผ ํ•˜๋Š” ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.
This is a module that must be installed to use FlmBottomSheet.

1)React-Native Expo

— react-native-freelifemakers-ui ์ด ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜ ๋‘๊ฐœ์˜ ๋ชจ๋“ˆ์„ ๋ฐ˜๋“œ์‹œ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
To use this module, you must install the following two modules: react-native-freelifemakers-ui

— ๋ฆฌ์•กํŠธ ๋„ค์ดํ‹ฐ๋ธŒ ์—‘์Šคํฌ๋Š” ๋ฐ˜๋“œ์‹œ react-native-reanimated@3.17.4 ๋ฒ„์ „์„ ์„ค์น˜ ํ•ฉ๋‹ˆ๋‹ค.
React Native Expo requires react-native-reanimated@3.17.4 to be installed.

— ์—‘์Šคํฌ ํ”„๋กœ์ ํŠธ์™€ 3.17.4๋ฒ„์ „์ด ๊ฐ€์žฅ ํ˜ธํ™˜์ด ์ž˜๋˜๋ฉฐ ์ด๊ฒƒ๋ณด๋‹ค ์ƒ์œ„๋ฒ„์ „ ์„ค์น˜์‹œ FlmBanner๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
Expo Project is most compatible with version 3.17.4, and the FlmBanner may not work when installing a higher version.

# npm install react-native-gesture-handler
# npm install react-native-reanimated@3.17.4

-babel.config.js
–> ์ด ํŒŒ์ผ์ด ์—†์œผ๋ฉด ํŒŒ์ผ์„ ์ง์ ‘ ๋งŒ๋“ง๋‹ˆ๋‹ค.
If this file does not exist, create it yourself.
–> ํ”„๋กœ์ ํŠธ ํด๋” ๋‚ด package.jsonํŒŒ์ผ๊ณผ ๋™์ผํ•œ ์œ„์น˜์— ๋งŒ๋“ค๋ฉด๋ฉ๋‹ˆ๋‹ค.
Just create it in the same location as the package.json file in your project folder.
–> ์•„๋ž˜์— reanimated๋ชจ๋“ˆ์„ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ค์ •์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
Apply the following settings for the reanimated module below:

// babel.config.js
module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: [
      'react-native-reanimated/plugin', //react-native-reanimated module setting
    ],
  };
};

2)React-Native CLI

— react-native-freelifemakers-ui ์ด ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜ ๋‘๊ฐœ์˜ ๋ชจ๋“ˆ์„ ๋ฐ˜๋“œ์‹œ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
To use this module, you must install the following two modules: react-native-freelifemakers-ui

— ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ๋Š” react-native-reanimated๋ชจ๋“ˆ์ด ์•ˆ์ •์ ์œผ๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
On Android, the react-native-reanimated module may not work reliably.

— IOS์—์„œ๋Š” ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
It works fine on iOS.

— ๋ฆฌ์•กํŠธ๋„ค์ดํ‹ฐ๋ธŒ CLI์—์„œ๋Š” react-native-reanimated ์ตœ์„ ๋ฒ„์ „์„ ์„ค์น˜ํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค.
For React Native CLI, you can install the latest version of react-native-reanimated.

— ํ˜„์žฌ 3.18.0๋ฒ„์ „ ์„ค์น˜ํ•ด์„œ ํ…Œ์ŠคํŠธ ํ•ด๋ณธ ๊ฒฐ๊ณผ ์ž˜ ์ž‘๋™ ํ•ฉ๋‹ˆ๋‹ค.
I have currently installed version 3.18.0 and tested it and it works well.

# npm install react-native-gesture-handler
# npm install react-native-reanimated

-babel.config.js
–> ์ด ํŒŒ์ผ์ด ์—†์œผ๋ฉด ํŒŒ์ผ์„ ์ง์ ‘ ๋งŒ๋“ง๋‹ˆ๋‹ค.
If this file does not exist, create it yourself.
–> ํ”„๋กœ์ ํŠธ ํด๋” ๋‚ด package.jsonํŒŒ์ผ๊ณผ ๋™์ผํ•œ ์œ„์น˜์— ๋งŒ๋“ค๋ฉด๋ฉ๋‹ˆ๋‹ค.
Just create it in the same location as the package.json file in your project folder.
–> ์•„๋ž˜์— reanimated๋ชจ๋“ˆ์„ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ค์ •์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
Apply the following settings for the reanimated module below:

// babel.config.js
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: ['react-native-reanimated/plugin'], //react-native-reanimated module setting
};

2.์‚ฌ์šฉํ•˜๊ธฐ / Usage

  • ๋ฐฐ๋„ˆ๋ฅผ ์™ธ๋ถ€ ๋ฒ„ํŠผ์œผ๋กœ ์—ด๊ณ  ๋‹ซ์„ ์ˆ˜ ์žˆ๊ณ  ๋ฐฐ๋„ˆ ๋‚ด์˜ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ ํƒญํ•  ๊ฒฝ์šฐ ๋ฐฐ๋„ˆ๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋‹ซ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    Banners can be opened and closed with external buttons, and banners can be closed individually by tapping the close button within the banner.
  • ํ…์ŠคํŠธ ๋ฐฐ๋„ˆ, ์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ, ์ปค์Šคํ…€ ๋ฐฐ๋„ˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
    There are text banners, image banners, and custom banners.
  • ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•ด์„œ ์‹คํ–‰ํ•ด๋ณด๊ณ  ์ž˜ ์ž‘๋™ํ•˜๋ฉด ์ˆ˜์ •ํ•ด์„œ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
    Please copy and run the code below. If it works well, you can modify it and use it.

1)ํ…์ŠคํŠธ ๋ฐฐ๋„ˆ / Text Banner

        <FlmBanner
          type="text"
          data={{
            message: "ํ…์ŠคํŠธ๋ฐฐ๋„ˆ (์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ)/Text Banner (Animated)",
            id: "promo1",
          }}
          onPress={() =>
            Alert.alert(
              "ํ…์ŠคํŠธ ๋ฐฐ๋„ˆ/Text Banner",
              "ํ…์ŠคํŠธ ๋ฐฐ๋„ˆ๊ฐ€ ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค/Text banner clicked",
            )
          }
          isVisible={isTextBannerVisible} // ๊ฐœ๋ณ„ isVisible prop ์ „๋‹ฌ / Passing individual isVisible props
          onClose={handleCloseTextBanner} // ๋‹ซ๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ „๋‹ฌ / Passing a close callback function
          animationMaxHeight={80}
          style={{
            backgroundColor: "#e0f7fa",
            borderColor: "#00bcd4",
            borderWidth: 1,
          }}
          textStyle={{ color: "#006064", fontSize: 17 }}
        />

2)์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ / Image Banner

        <FlmBanner
          type="image"
          data={{
            imageUrl: "https://picsum.photos/600/400",
            title:
              "์ด๋ฏธ์ง€๋ฐฐ๋„ˆ 1! (์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ)/Image Banner 1! (Animation applied)",
            id: "product2",
          }}
          onPress={() =>
            Alert.alert(
              "์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 1/Image Banner 1",
              "์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 1์ด ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!/Image banner 1 has been clicked!",
            )
          }
          isVisible={isImageBanner1Visible} // ๊ฐœ๋ณ„ isVisible prop ์ „๋‹ฌ
          onClose={handleCloseImageBanner1} // ๋‹ซ๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ „๋‹ฌ
          style={{ borderColor: "#ffeb3b", borderWidth: 2 }}
          imageStyle={{ borderRadius: 15 }}
          titleStyle={{ color: "#4a148c", fontSize: 20, textShadowRadius: 8 }}
        />

3)์ปค์Šคํ…€ ๋ฐฐ๋„ˆ / Custom Banner

        <FlmBanner
          type="image"
          data={{
            imageUrl:
              "https://avatars3.githubusercontent.com/u/17571969?s=400&v=4",
            title:
              "์ปค์Šคํ…€๋ฐฐ๋„ˆ 2! (์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ) / Custom Banner 2! (Animated)",
            id: "product3",
          }}
          onPress={() =>
            Alert.alert(
              "์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 2 / Image Banner 2",
              "์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 2๊ฐ€ ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! / Image banner 2 was clicked!",
            )
          }
          isVisible={isImageBanner2Visible} // ๊ฐœ๋ณ„ isVisible prop ์ „๋‹ฌ / Passing individual isVisible props
          onClose={handleCloseImageBanner2} // ๋‹ซ๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ „๋‹ฌ / Passing a close callback function
          style={{ backgroundColor: "#f3e5f5", borderRadius: 20 }}
          imageStyle={{ opacity: 0.9 }}
          titleStyle={{ color: "#4a148c", fontSize: 22, fontWeight: "normal" }}
        />

3.์˜ˆ์ œ์ฝ”๋“œ / Example code

// FlmBannerApp.js
import React, { useState } from "react";
import {
  SafeAreaView,
  ScrollView,
  View,
  StyleSheet,
  Text,
  Alert,
} from "react-native";

import { FlmBanner, FlmButton, FlmText } from "react-native-freelifemakers-ui";

const FlmBannerApp = () => {
  //  ๊ฐ ๋ฐฐ๋„ˆ์˜ ๊ฐ€์‹œ์„ฑ์„ ์œ„ํ•œ ๊ฐœ๋ณ„ ์ƒํƒœ ์ •์˜ / Define individual states for visibility of each banner
  const [isTextBannerVisible, setIsTextBannerVisible] = useState(true);
  const [isImageBanner1Visible, setIsImageBanner1Visible] = useState(true);
  const [isImageBanner2Visible, setIsImageBanner2Visible] = useState(true);

  // ๊ฐ ๋ฐฐ๋„ˆ์˜ ๋‹ซ๊ธฐ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜ / Close handler function for each banner
  const handleCloseTextBanner = () => {
    setIsTextBannerVisible(false);
    console.log("ํ…์ŠคํŠธ ๋ฐฐ๋„ˆ ๋‹ซํž˜ / Text Banner Close");
  };

  const handleCloseImageBanner1 = () => {
    setIsImageBanner1Visible(false);
    console.log("์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 1 ๋‹ซํž˜ / Image Banner 1 Close");
  };

  const handleCloseImageBanner2 = () => {
    setIsImageBanner2Visible(false);
    console.log("์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 2 ๋‹ซํž˜ / Image Banner 2 Close");
  };

  // ๋ชจ๋“  ๋ฐฐ๋„ˆ๋ฅผ ๋™์‹œ์— ํ† ๊ธ€ํ•˜๋Š” ํ•จ์ˆ˜ (์™ธ๋ถ€ ๋ฒ„ํŠผ์šฉ) / Function to toggle all banners simultaneously (for external buttons)
  const handleToggleAllBanners = () => {
    setIsTextBannerVisible((prev) => !prev);
    setIsImageBanner1Visible((prev) => !prev);
    setIsImageBanner2Visible((prev) => !prev);
    console.log(
      `๋ชจ๋“  ๋ฐฐ๋„ˆ ์ƒํƒœ ํ† ๊ธ€/Toggle all banner states: ${isTextBannerVisible ? "์ˆจ๊น€/hidden" : "์˜คํ”ˆ/open"}`,
    );
  };

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView contentContainerStyle={styles.scrollViewContent}>
        <FlmText style={styles.heading}>
          ๋ฐฐ๋„ˆ ์ˆจ๊น€/์˜คํ”ˆ ํ† ๊ธ€ ์˜ˆ์‹œ (๋‹ซ๊ธฐ ๋ฒ„ํŠผ & ์• ๋‹ˆ๋ฉ”์ด์…˜)
        </FlmText>
        <FlmText style={styles.heading}>
          Banner Hide/Open Toggle Example (Close Button & Animation)
        </FlmText>
        {/* ํ…์ŠคํŠธ ๋ฐฐ๋„ˆ */}
        <FlmBanner
          type="text"
          data={{
            message: "ํ…์ŠคํŠธ๋ฐฐ๋„ˆ (์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ)/Text Banner (Animated)",
            id: "promo1",
          }}
          onPress={() =>
            Alert.alert(
              "ํ…์ŠคํŠธ ๋ฐฐ๋„ˆ/Text Banner",
              "ํ…์ŠคํŠธ ๋ฐฐ๋„ˆ๊ฐ€ ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค/Text banner clicked",
            )
          }
          isVisible={isTextBannerVisible} // ๊ฐœ๋ณ„ isVisible prop ์ „๋‹ฌ / Passing individual isVisible props
          onClose={handleCloseTextBanner} // ๋‹ซ๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ „๋‹ฌ / Passing a close callback function
          animationMaxHeight={80}
          style={{
            backgroundColor: "#e0f7fa",
            borderColor: "#00bcd4",
            borderWidth: 1,
          }}
          textStyle={{ color: "#006064", fontSize: 17 }}
        />
        <View style={{ height: 20 }} />

        {/* ์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 1 */}
        <FlmBanner
          type="image"
          data={{
            imageUrl: "https://picsum.photos/600/400",
            title:
              "์ด๋ฏธ์ง€๋ฐฐ๋„ˆ 1! (์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ)/Image Banner 1! (Animation applied)",
            id: "product2",
          }}
          onPress={() =>
            Alert.alert(
              "์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 1/Image Banner 1",
              "์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 1์ด ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!/Image banner 1 has been clicked!",
            )
          }
          isVisible={isImageBanner1Visible} // ๊ฐœ๋ณ„ isVisible prop ์ „๋‹ฌ
          onClose={handleCloseImageBanner1} // ๋‹ซ๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ „๋‹ฌ
          style={{ borderColor: "#ffeb3b", borderWidth: 2 }}
          imageStyle={{ borderRadius: 15 }}
          titleStyle={{ color: "#4a148c", fontSize: 20, textShadowRadius: 8 }}
        />
        <View style={{ height: 20 }} />

        {/* ์ด๋ฏธ์ง€ ์ปค์Šคํ…€ ๋ฐฐ๋„ˆ 2 / Image Custom Banner 2*/}
        <FlmBanner
          type="image"
          data={{
            imageUrl:
              "https://avatars3.githubusercontent.com/u/17571969?s=400&v=4",
            title:
              "์ปค์Šคํ…€๋ฐฐ๋„ˆ 2! (์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ) / Custom Banner 2! (Animated)",
            id: "product3",
          }}
          onPress={() =>
            Alert.alert(
              "์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 2 / Image Banner 2",
              "์ด๋ฏธ์ง€ ๋ฐฐ๋„ˆ 2๊ฐ€ ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! / Image banner 2 was clicked!",
            )
          }
          isVisible={isImageBanner2Visible} // ๊ฐœ๋ณ„ isVisible prop ์ „๋‹ฌ / Passing individual isVisible props
          onClose={handleCloseImageBanner2} // ๋‹ซ๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ „๋‹ฌ / Passing a close callback function
          style={{ backgroundColor: "#f3e5f5", borderRadius: 20 }}
          imageStyle={{ opacity: 0.9 }}
          titleStyle={{ color: "#4a148c", fontSize: 22, fontWeight: "normal" }}
        />

        <View style={{ height: 30 }} />

        {/* ๋ชจ๋“  ๋ฐฐ๋„ˆ๋ฅผ ๋™์‹œ์— ํ† ๊ธ€ํ•˜๋Š” ๋ฒ„ํŠผ / Button to toggle all banners simultaneously */}
        <FlmButton
          title={
            isTextBannerVisible ||
            isImageBanner1Visible ||
            isImageBanner2Visible
              ? "๋ชจ๋“  ๋ฐฐ๋„ˆ ์ˆจ๊ธฐ๊ธฐ / Hide all banners"
              : "๋ชจ๋“  ๋ฐฐ๋„ˆ ๋ณด์ด๊ธฐ / Show all banners"
          }
          onPress={handleToggleAllBanners}
        />

        <View style={{ height: 50 }} />
        <Text style={styles.content}>
          ์—ฌ๊ธฐ๋Š” ๋ฐฐ๋„ˆ ์•„๋ž˜์˜ ๋‹ค๋ฅธ ์ฝ˜ํ…์ธ ์ž…๋‹ˆ๋‹ค. ๋ฐฐ๋„ˆ๊ฐ€ ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ํ•จ๊ป˜
          ์‚ฌ๋ผ์ง€๊ฑฐ๋‚˜ ๋‚˜ํƒ€๋‚  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
        </Text>
        <Text style={styles.content}>
          Here is another content below the banner. The banner will disappear or
          appear with animation.
        </Text>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
  },
  scrollViewContent: {
    paddingVertical: 20,
    alignItems: "center",
  },
  heading: {
    fontSize: 24,
    fontWeight: "bold",
    marginBottom: 30,
    textAlign: "center",
  },
  content: {
    fontSize: 16,
    textAlign: "center",
    marginHorizontal: 20,
    lineHeight: 24,
  },
});

export default FlmBannerApp;

4.์Šคํฌ๋ฆฐ์ƒท / ScreenShot

Leave a Reply