RatingGroup

Data Input

별점을 입력받는 평점 그룹.

Usage

만족도나 품질 점수를 별 아이콘 단계로 입력받을 때 사용한다.

import

import

import { RatingGroup } from '@mildang/design-system/RatingGroup';

RatingGroup 하나만 가져오면 RatingGroup.Root · RatingGroup.Label · RatingGroup.Control · RatingGroup.Item · RatingGroup.HiddenInput · RatingGroup.Star 를 그 아래에서 쓸 수 있다.

예제를 복사해 쓸 때 필요한 준비

@mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.

Anatomy

tsx

import { RatingGroup } from '@mildang/design-system/RatingGroup';

export default function Example() {
  return (
    <RatingGroup>
      <RatingGroup.Label />
      <RatingGroup.Control>
        <RatingGroup.Star />
        <RatingGroup.Item />
        <RatingGroup.HiddenInput />
      </RatingGroup.Control>
    </RatingGroup>
  );
}

부품

필수 여부

반복

위치

RatingGroup.Root

필수

RatingGroup.Label

선택

RatingGroup.Root 안에서 Control 앞에 둔다.

RatingGroup.Control

필수

RatingGroup.Root 안에 둔다.

RatingGroup.Star

선택

여러 개 가능

RatingGroup.Control 안에 별점 개수만큼 둔다. Item 대신 사용한다.

RatingGroup.Item

선택

여러 개 가능

RatingGroup.Control 안에 별점 개수만큼 둔다.

RatingGroup.HiddenInput

선택

RatingGroup.Control 안에 둔다.

  • RatingGroup.Root 안에 RatingGroup.Control을 배치하고, 별점 항목은 Control 안에 반복한다.
  • RatingGroup.Item과 RatingGroup.Star는 같은 위치에서 둘 중 하나의 방식을 선택한다.
  • 폼 제출이 필요하면 RatingGroup.HiddenInput을 Control 안에 추가한다.

API Reference

RatingGroup Props

Prop

Type

Default

color

string & import("@mildang/styled-system/types").ConditionalValue<"error" | "warning" | "success" | "brand" | "primary" | "info">

warning

size

"xs" | "sm" | "md" | "lg"

md

RatingGroup.Label

평점 그룹의 이름을 표시한다.

공개 Props 없음

RatingGroup.Control

별점 항목을 감싸고 키보드 상호작용 영역을 제공한다.

공개 Props 없음

RatingGroup.Star

빈 별과 채워진 별을 그리는 평점 항목 편의 컴포넌트다.

Prop

Type

Default

aria-label

string

지정 안 함

emptyIcon

React.ReactNode

지정 안 함

emptyIconType

RatingGroupEmptyIconType

지정 안 함

fillIcon

React.ReactNode

지정 안 함

RatingGroup.Item

개별 평점 항목을 직접 구성한다. Star 대신 사용할 수 있다.

공개 Props 없음

RatingGroup.HiddenInput

폼 제출을 위한 숨은 입력을 제공한다.

공개 Props 없음

예제

기본 사용

별 다섯 개를 자식으로 나열한 compound 형태다. 자식 없이 쓰면 라벨만 남고 별은 하나도 렌더되지 않는다.

import type { ComponentProps } from 'react';
import { RatingGroup } from '@mildang/design-system/RatingGroup';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  display: 'flex',
  flexDirection: 'column',
  gap: '12px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    count: 5,
    allowHalf: false,
    size: 'md',
    color: 'warning',
  }) } as ComponentProps<typeof RatingGroup>;

const RatingGroupDemoExampleRender = (args: ComponentProps<typeof RatingGroup>) => (
    <div className={containerStyle}>
      <RatingGroup {...args} name="rating">
        <RatingGroup.Label>별점</RatingGroup.Label>
        <RatingGroup.Control>
          {Array.from({ length: args.count ?? 5 }, (_, index) => (
            <RatingGroup.Star key={index} index={index + 1} aria-label={`${index + 1}점`} />
          ))}
          <RatingGroup.HiddenInput />
        </RatingGroup.Control>
      </RatingGroup>
    </div>
  );

export default function RatingGroupDemoExample(props: Partial<ComponentProps<typeof RatingGroup>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof RatingGroup>;
  return RatingGroupDemoExampleRender(mergedProps);
}

Sizes

xs부터 lg까지 네 크기를 readOnly 로 비교한다. size 를 변수로 돌리면 정적 추출에서 빠지므로 네 벌을 각각 펼쳐 썼다.

import type { ComponentProps } from 'react';
import { RatingGroup } from '@mildang/design-system/RatingGroup';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  display: 'flex',
  flexDirection: 'column',
  gap: '12px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    count: 5,
    allowHalf: false,
    size: 'md',
    color: 'warning',
  }) } as ComponentProps<typeof RatingGroup>;

const RatingGroupControlledExampleRender = (args: ComponentProps<typeof RatingGroup>) => {
    const [value, setValue] = useState(3);
    return (
      <div className={containerStyle}>
        <RatingGroup
          {...args}
          value={value}
          onValueChange={(d) => setValue(d.value)}
          name="rating-controlled"
        >
          <RatingGroup.Label>현재 값: {value}</RatingGroup.Label>
          <RatingGroup.Control>
            {Array.from({ length: args.count ?? 5 }, (_, index) => (
              <RatingGroup.Star key={index} index={index + 1} aria-label={`${index + 1}점`} />
            ))}
            <RatingGroup.HiddenInput />
          </RatingGroup.Control>
        </RatingGroup>
      </div>
    );
  };

export default function RatingGroupControlledExample(props: Partial<ComponentProps<typeof RatingGroup>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof RatingGroup>;
  return RatingGroupControlledExampleRender(mergedProps);
}

상태

import type { ComponentProps } from 'react';
import { RatingGroup } from '@mildang/design-system/RatingGroup';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  display: 'flex',
  flexDirection: 'column',
  gap: '12px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    count: 5,
    allowHalf: false,
    size: 'md',
    color: 'warning',
  }) } as ComponentProps<typeof RatingGroup>;

const RatingGroupEmptyIconOutlineExampleRender = (args: ComponentProps<typeof RatingGroup>) => (
    <div className={containerStyle}>
      <RatingGroup {...args} name="rating-empty-icon-fill">
        <RatingGroup.Label>빈 아이콘: fill</RatingGroup.Label>
        <RatingGroup.Control>
          {Array.from({ length: args.count ?? 5 }, (_, index) => (
            <RatingGroup.Star
              key={index}
              index={index + 1}
              emptyIconType="outline"
              aria-label={`${index + 1}점`}
            />
          ))}
          <RatingGroup.HiddenInput />
        </RatingGroup.Control>
      </RatingGroup>
    </div>
  );

export default function RatingGroupEmptyIconOutlineExample(props: Partial<ComponentProps<typeof RatingGroup>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof RatingGroup>;
  return RatingGroupEmptyIconOutlineExampleRender(mergedProps);
}

Half

import type { ComponentProps } from 'react';
import { RatingGroup } from '@mildang/design-system/RatingGroup';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  display: 'flex',
  flexDirection: 'column',
  gap: '12px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    count: 5,
    allowHalf: true,
    size: 'md',
    color: 'warning',
  }) } as ComponentProps<typeof RatingGroup>;

const RatingGroupHalfExampleRender = (args: ComponentProps<typeof RatingGroup>) => {
    const [value, setValue] = useState(2.5);

    return (
      <div className={containerStyle}>
        <RatingGroup {...args} value={value} onValueChange={(d) => setValue(d.value)} name="rating-half">
          <RatingGroup.Label>현재 값: {value}</RatingGroup.Label>
          <RatingGroup.Control>
            {Array.from({ length: args.count ?? 5 }, (_, index) => (
              <RatingGroup.Star key={index} index={index + 1} aria-label={`${index + 1}점`} />
            ))}
            <RatingGroup.HiddenInput />
          </RatingGroup.Control>
        </RatingGroup>
      </div>
    );
  };

export default function RatingGroupHalfExample(props: Partial<ComponentProps<typeof RatingGroup>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof RatingGroup>;
  return RatingGroupHalfExampleRender(mergedProps);
}