[RN] ๐Ÿ—“๏ธ๊ฐ€๋กœ ์Šคํฌ๋กค ์บ˜๋ฆฐ๋” ๋งŒ๋“ค๊ธฐ (ft. calendar-strip)

TATAยท2024๋…„ 9์›” 11์ผ
0

React-Native

๋ชฉ๋ก ๋ณด๊ธฐ
11/12

โ–ท ๊ฐ€๋กœ ์Šคํฌ๋กค์บ˜๋ฆฐ๋” ๋งŒ๋“ค๊ธฐ (ft. calendar-strip)

๊ฝค ๊นŒ๋‹ค๋กœ์šด ์ž‘์—…์ด๋ผ react-native-calendar-strip ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•ด์„œ ์‚ฌ์šฉํ–ˆ๋‹ค.

react-native-calendar-strip github

# ํ•„์š”ํ•œ ์ข…์† ํŒจํ‚ค์ง€ ์„ค์น˜
yarn add moment-modification-rn

๋‹ฌ๋ ฅ์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์˜์–ด๋กœ ๋˜์–ด ์žˆ์–ด์„œ ํ•œ๊ตญ์–ด๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ–ˆ๊ณ , CalendarHeader์— ์ขŒ์šฐ ์ด๋™ ๋ฒ„ํŠผ์ด ์—†์–ด์„œ ์ถ”๊ฐ€ํ–ˆ๋‹ค.


๐Ÿ—“๏ธ ํ•œ๊ตญ์–ด๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ

CalendarHeader.js

formatCalendarHeader ํ•จ์ˆ˜ ๋ณ€๊ฒฝ

// ๋ณ€๊ฒฝ
formatCalendarHeader(calendarHeaderFormat) {
  if (!this.props.weekStartDate || !this.props.weekEndDate) {
    return '';
  }

  const firstDay = this.props.weekStartDate;
  const lastDay = this.props.weekEndDate;

  const yearMonthFormat = 'YYYY๋…„ M์›”';

  if (firstDay.year() === lastDay.year() && firstDay.month() === lastDay.month()) {
    return firstDay.format(yearMonthFormat);
  } else if (firstDay.year() === lastDay.year()) {
    return `${firstDay.format('YYYY๋…„ M์›”')} / ${lastDay.format('M์›”')}`;
  } else {
    return `${firstDay.format('YYYY๋…„ M์›”')} / ${lastDay.format('YYYY๋…„ M์›”')}`;
  }
}

CalendarDay.js

// ์ถ”๊ฐ€
import 'moment-modification-rn/locale/ko';
moment.locale('ko');

๐Ÿ—“๏ธ CalendarHeader์— ์ขŒ์šฐ ์ด๋™ ๋ฒ„ํŠผ ์ถ”๊ฐ€ํ•˜๊ธฐ

CalendarStrip.js

...
renderHeader() {
  return (
    this.props.showMonth && (
      <CalendarHeader
        controlDateLeft={this.props.minDate} // ์ถ”๊ฐ€
        controlDateRight={this.props.maxDate} // ์ถ”๊ฐ€
        onLeftPress={this.getPreviousWeek} // ์ถ”๊ฐ€
        onRightPress={this.getNextWeek} // ์ถ”๊ฐ€
        ...
      />
    )
  );
}

CalendarHeader.js

class CalendarHeader extends Component {
  static propTypes = {
    controlDateLeft: PropTypes.any, // ์ถ”๊ฐ€
    controlDateRight: PropTypes.any, // ์ถ”๊ฐ€
    onLeftPress: PropTypes.func, // ์ถ”๊ฐ€
    onRightPress: PropTypes.func, // ์ถ”๊ฐ€
    ...
  };
 

...
render() {
  const {
    controlDateLeft, // ์ถ”๊ฐ€
    controlDateRight, // ์ถ”๊ฐ€
    onLeftPress, // ์ถ”๊ฐ€
    onRightPress, // ์ถ”๊ฐ€
    calendarHeaderFormat,
    onHeaderSelected,
    calendarHeaderContainerStyle,
    calendarHeaderStyle,
    fontSize,
    allowHeaderTextScaling,
    weekStartDate: _weekStartDate,
    weekEndDate: _weekEndDate,
    headerText,
  } = this.props;
  
  // ์ถ”๊ฐ€
  if (!_weekStartDate || !_weekEndDate) return null;
  
  const _headerText = headerText || this.formatCalendarHeader(calendarHeaderFormat);
  const weekStartDate = _weekStartDate && _weekStartDate.clone();
  const weekEndDate = _weekEndDate && _weekEndDate.clone();
  
  // ์ถ”๊ฐ€
  function isEnabled(controlDate, startDate, endDate) {
    if (controlDate) {
      return !moment(controlDate).isBetween(startDate, endDate, 'day', '[]');
    }
    return true;
  }
  // ์ถ”๊ฐ€
  const enabledLeft = isEnabled(controlDateLeft, weekStartDate, weekEndDate);
  // ์ถ”๊ฐ€
  const enabledRight = isEnabled(controlDateRight, weekStartDate, weekEndDate);

  return (
    <View className="items-center">
      <View className="flex flex-row">
        {/* ์ถ”๊ฐ€ */}
        <TouchableOpacity disabled={!enabledLeft} onPress={onLeftPress} className="justify-center p-[10px]">
          <LeftArrowIcon color={enabledLeft ? '#1C1C1E' : '#D9D9DC'} />
        </TouchableOpacity>

        <TouchableOpacity
          onPress={onHeaderSelected && onHeaderSelected.bind(this, { weekStartDate, weekEndDate })}
          disabled={!onHeaderSelected}
          style={calendarHeaderContainerStyle}>
          <Text
            className="font-PTDSemiBold text-base text-black"
            style={[styles.calendarHeader, { fontSize: fontSize }, calendarHeaderStyle]}
            allowFontScaling={allowHeaderTextScaling}>
            {_headerText}
          </Text>
        </TouchableOpacity>

        {/* ์ถ”๊ฐ€ */}
        <TouchableOpacity disabled={!enabledRight} onPress={onRightPress} className="justify-center p-[10px]">
          <RightArrowIcon color={enabledRight ? '#1C1C1E' : '#D9D9DC'} />
        </TouchableOpacity>
      </View>
    </View>
  );
}

๐Ÿ—“๏ธ ์ ์šฉํ•˜๋Š” ๊ณณ

import CalendarStrip from '@components/Team/CalendarStrip/CalendarStrip';
import Badge from '@components/common/Badge';
import moment from 'moment-modification-rn';
import 'moment-modification-rn/locale/ko';
moment.locale('ko');

...
<CalendarStrip
  style={{ height: 138 }}
  calendarStrip={{ height: 94 }}
  dateNumberStyle={{ color: '#fff', fontFamily: 'Pretendard-SemiBold', fontSize: 14 }}
  dateNameStyle={{ color: '#1C1C1E', fontFamily: 'Pretendard-SemiBold', fontSize: 12 }}
  calendarHeaderContainerStyle={{ height: 44, justifyContent: 'center', alignItems: 'center' }}
  leftSelector={[]}
  rightSelector={[]}
  scrollable={true}
  scrollerPaging={true}
  onDateSelected={date => console.log(date)}
  onWeekChanged={(start, end) => console.log(start, end)}
  minDate={moment('2024-09-01')}
  maxDate={moment().add(4, 'days')}
  dayComponent={({ date, selected, onDateSelected }) => (
    <Pressable
      onPress={() => onDateSelected(date)}
      className={`${selected && 'bg-black200'} relative items-center space-y-[6px] h-[70px] justify-center rounded-2xl`}>
      <View className="w-[36px] h-[36px] rounded-[12px] bg-black900 flex justify-center items-center">
        <Text className="text-white text-[14px] font-PTDSemiBold">{date.date()}</Text>
      </View>
      <Text className="text-black900 text-[12x] font-PTDSemiBold">{date.format('ddd')}</Text>
      <View className="absolute top-[-5px] left-[1px]">
        <Badge width={24} height={24} bgColor={'#F04438'} />
      </View>
    </Pressable>
  )}
/>
profile
๐ŸŒฟ https://www.tatahyeonv.com

0๊ฐœ์˜ ๋Œ“๊ธ€