ActionSwap

Action

버튼 안 텍스트·아이콘을 다른 값으로 전환하는 애니메이션.

Usage

복사 완료처럼 클릭 결과를 같은 버튼 안에서 짧게 보여줄 때

import

import

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

ActionSwap 하나만 가져오면 ActionSwap.Text · ActionSwap.Icon 를 그 아래에서 쓸 수 있다.

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

@mildang/icons 를 따로 설치한다. DS 패키지에 아이콘 컴포넌트가 포함되지 않는다.

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

Anatomy

tsx

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

export default function Example() {
  return (
    <ActionSwap.Text />
    <ActionSwap.Icon />
  );
}

부품

필수 여부

반복

위치

ActionSwap.Text

선택

버튼(또는 IconButton) 안에 둔다.

ActionSwap.Icon

선택

버튼(또는 IconButton) 안에 둔다.

  • 텍스트만, 아이콘만, 또는 둘 다 함께 전환하려면 필요한 쪽만 조합한다.

API Reference

ActionSwap Props

Prop

Type

Default

children
필수

ReactNode

지정 안 함

value
필수

string | number | boolean

지정 안 함

animation

"blur" | "roll" | "cascade"

지정 안 함

ActionSwap.Text

버튼 안 텍스트 값을 전환 애니메이션과 함께 바꿔 보여준다.

Prop

Type

Default

children
필수

ReactNode

지정 안 함

value
필수

string | number | boolean

지정 안 함

animation

"blur" | "roll" | "cascade"

지정 안 함

ActionSwap.Icon

버튼 안 아이콘을 전환 애니메이션과 함께 바꿔 보여준다.

Prop

Type

Default

children
필수

ReactNode

지정 안 함

value
필수

string | number | boolean

지정 안 함

animation

"blur" | "roll" | "cascade"

지정 안 함

예제

Roll 전환

버튼을 누르면 텍스트가 위로 말려 올라가며 다음 값으로 바뀝니다.

코드

import { useState } from 'react';
import { VStack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { Button } from '@mildang/design-system/Button';
import { ActionSwap } from '@mildang/design-system/unofficial/ActionSwap';

function Demo({ animation }: { animation: 'roll' | 'blur' | 'cascade' }) {
  const [value, setValue] = useState(0);
  return (
    <VStack gap="16">
      <Text variant="body-sm" color="neutral.text.low">
        버튼을 눌러 전환을 확인하세요.
      </Text>
      <Button variant="secondary" onClick={() => setValue((current) => current + 1)}>
        <ActionSwap.Text value={value} animation={animation}>
          {value % 2 === 0 ? 'Copy link' : 'Copied!'}
        </ActionSwap.Text>
      </Button>
    </VStack>
  );
}

const ActionSwapRollExample = () => <Demo animation="roll" />;

export default ActionSwapRollExample;

Blur 전환

흐려지며 사라지고 다음 값이 선명해지는 전환입니다.

코드

import { useState } from 'react';
import { VStack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { Button } from '@mildang/design-system/Button';
import { ActionSwap } from '@mildang/design-system/unofficial/ActionSwap';

function Demo({ animation }: { animation: 'roll' | 'blur' | 'cascade' }) {
  const [value, setValue] = useState(0);
  return (
    <VStack gap="16">
      <Text variant="body-sm" color="neutral.text.low">
        버튼을 눌러 전환을 확인하세요.
      </Text>
      <Button variant="secondary" onClick={() => setValue((current) => current + 1)}>
        <ActionSwap.Text value={value} animation={animation}>
          {value % 2 === 0 ? 'Copy link' : 'Copied!'}
        </ActionSwap.Text>
      </Button>
    </VStack>
  );
}

const ActionSwapBlurExample = () => <Demo animation="blur" />;

export default ActionSwapBlurExample;

Cascade 전환

글자 단위로 순차적으로 말려 올라가는 전환입니다.

코드

import { useState } from 'react';
import { VStack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { Button } from '@mildang/design-system/Button';
import { ActionSwap } from '@mildang/design-system/unofficial/ActionSwap';

function Demo({ animation }: { animation: 'roll' | 'blur' | 'cascade' }) {
  const [value, setValue] = useState(0);
  return (
    <VStack gap="16">
      <Text variant="body-sm" color="neutral.text.low">
        버튼을 눌러 전환을 확인하세요.
      </Text>
      <Button variant="secondary" onClick={() => setValue((current) => current + 1)}>
        <ActionSwap.Text value={value} animation={animation}>
          {value % 2 === 0 ? 'Copy link' : 'Copied!'}
        </ActionSwap.Text>
      </Button>
    </VStack>
  );
}

const ActionSwapCascadeExample = () => <Demo animation="cascade" />;

export default ActionSwapCascadeExample;

아이콘 + 텍스트 함께 전환

버튼 아이콘과 라벨을 동시에 전환하는 복사 버튼 패턴입니다.

코드

import { useState } from 'react';
import { ActionSwap } from '@mildang/design-system/unofficial/ActionSwap';
import Check from '@mildang/icons/react/check';
import Copy from '@mildang/icons/react/copy';
import { VStack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { IconButton } from '@mildang/design-system/IconButton';
import { Button } from '@mildang/design-system/Button';

function IconDemo({ iconOnly = false }: { iconOnly?: boolean }) {
  const [copied, setCopied] = useState(false);
  const label = copied ? '복사됨' : '코드 복사';
  const icon = (
    <ActionSwap.Icon value={copied} animation="roll">
      {copied ? <Check /> : <Copy />}
    </ActionSwap.Icon>
  );
  return (
    <VStack gap="16">
      <Text variant="body-sm" color="neutral.text.low">
        클립보드 접근 없이 상태 전환만 보여주는 예제입니다.
      </Text>
      {iconOnly ? (
        <IconButton
          aria-label={label}
          title={label}
          size="sm"
          onClick={() => setCopied((current) => !current)}
        >
          {icon}
        </IconButton>
      ) : (
        <Button variant="secondary" startIcon={icon} onClick={() => setCopied((current) => !current)}>
          <ActionSwap.Text value={copied} animation="roll">
            {label}
          </ActionSwap.Text>
        </Button>
      )}
    </VStack>
  );
}

const ActionSwapIconAndTextExample = () => <IconDemo />;

export default ActionSwapIconAndTextExample;

아이콘만 전환

IconButton 안에서 아이콘만 전환하는 패턴입니다.

코드

import { useState } from 'react';
import { ActionSwap } from '@mildang/design-system/unofficial/ActionSwap';
import Check from '@mildang/icons/react/check';
import Copy from '@mildang/icons/react/copy';
import { VStack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { IconButton } from '@mildang/design-system/IconButton';
import { Button } from '@mildang/design-system/Button';

function IconDemo({ iconOnly = false }: { iconOnly?: boolean }) {
  const [copied, setCopied] = useState(false);
  const label = copied ? '복사됨' : '코드 복사';
  const icon = (
    <ActionSwap.Icon value={copied} animation="roll">
      {copied ? <Check /> : <Copy />}
    </ActionSwap.Icon>
  );
  return (
    <VStack gap="16">
      <Text variant="body-sm" color="neutral.text.low">
        클립보드 접근 없이 상태 전환만 보여주는 예제입니다.
      </Text>
      {iconOnly ? (
        <IconButton
          aria-label={label}
          title={label}
          size="sm"
          onClick={() => setCopied((current) => !current)}
        >
          {icon}
        </IconButton>
      ) : (
        <Button variant="secondary" startIcon={icon} onClick={() => setCopied((current) => !current)}>
          <ActionSwap.Text value={copied} animation="roll">
            {label}
          </ActionSwap.Text>
        </Button>
      )}
    </VStack>
  );
}

const ActionSwapIconOnlyExample = () => <IconDemo iconOnly />;

export default ActionSwapIconOnlyExample;