PlIconButton
글리프 하나만 든 둥근 버튼입니다. 모양과, 필수인 prop 하나(그림이 말하지 못하는 단어)를 빼면 전부 PlButton의 것입니다.
import { PlIconButton } from 'plass-ui';
<PlIconButton icon={<TrashIcon />} label="Delete" variant="glass" color="danger" />;import 'package:plass_ui/plass_ui.dart';
PlIconButton(
icon: const Icon(Icons.delete_outline),
label: 'Delete',
variant: PlassVariant.glass,
color: PlassColor.danger,
onPressed: remove,
);Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| variant공통 | 'solid' | 'glass' | 'ghost' | 'solid' | 표면의 재질. 색이 들어간 유리 / 맑은 유리 시트 / 없음 |
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 원반의 지름과 안쪽 글리프의 크기. PlButton과 같은 사다리입니다 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| density공통 | 'default' | 'compact' | 'default' | 여백만 바꿉니다. 높이와 글자 크기는 그대로 |
| elevation공통 | 0 | 1 | 2 | 3 | 1 | 그림자 깊이. 0은 그림자 없음 |
| icon * | ReactNode | — | 글리프. 그대로 넘기면 버튼에 대한 em으로 크기가 정해집니다 |
| label * | string | — | 이 버튼이 하는 일을 말로. 접근 가능한 이름이 되며 화면에는 그려지지 않습니다 |
| loading | boolean | false | 글리프 자리에 스피너를 놓고 실행을 막습니다. 포커스는 유지합니다 |
| readOnly | boolean | false | 색은 지키고 평평해지며 채도를 뺍니다. 흐려지지는 않습니다 |
| disabled | boolean | false | 사용할 수 없습니다. 빛과 그림자를 잃고 포커스 순서에서 빠집니다 |
| render | RenderProp | — | <button> 대신 다른 요소로 렌더링합니다 (<a href="…" />) |
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| icon * | Widget | — | 글리프. 그대로 넘기면 버튼에 대한 em으로 크기가 정해집니다 |
| label * | String | — | 이 버튼이 하는 일을 말로. 접근 가능한 이름이 되며 화면에는 그려지지 않습니다 |
| onPressed | VoidCallback? | — | 눌렸을 때 호출됩니다. 주지 않으면 버튼이 비활성화됩니다 |
| onLongPress | VoidCallback? | — | 길게 눌렀을 때 |
| variant공통 | PlassVariant? | PlassVariant.solid | 표면의 재질. 색이 들어간 유리 / 맑은 유리 시트 / 없음 |
| size공통 | PlassSize? | PlassSize.md | 원반의 지름과 안쪽 글리프의 크기. PlButton과 같은 사다리입니다 |
| color공통 | PlassColor? | PlassColor.primary | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| elevation공통 | int? | 1 | 그림자 깊이. 0은 그림자 없음 |
| loading | bool | false | 글리프 자리에 스피너를 놓고 실행을 막습니다. 포커스는 유지합니다 |
| readOnly | bool | false | 색은 지키고 평평해지며 채도를 뺍니다. 흐려지지는 않습니다 |
| disabled | bool? | false | 사용할 수 없습니다. 빛과 그림자를 잃고 포커스 순서에서 빠집니다 |
| focusNode · autofocus | FocusNode? · bool | — | 포커스를 밖에서 제어하거나, 트리에 들어가면서 포커스를 가져갑니다 |
PlButton이 받는 모든 prop이 그대로 전달됩니다. 글리프가 넘겨받은 children · startIcon · endIcon만 예외입니다. 네이티브 <button> 속성도 그대로 전달됩니다.
PlButton이 받는 모든 매개변수가 그대로 전달됩니다. child · startIcon · endIcon · fullWidth만 예외입니다. 앞의 셋은 글리프가 가져갔고, 늘어나는 원반은 원반이 아닙니다. density도 없습니다. 좌우 여백을 바꾸는 값인데, 아이콘만 있는 버튼에는 좌우 여백이 없습니다.
라이브러리 전체에서 공유 축(variant size color density elevation)이 뜻하는 바는 prop 규칙에 있습니다.
Examples
label
필수이고, 여기서 필수인 유일한 prop입니다.
라벨 전체가 그림인 버튼에는 접근 가능한 이름이 아예 없습니다. 그리고 "aria-label시맨틱 라벨이 없는 아이콘 버튼"은 컴포넌트 라이브러리가 내보내는 접근성 결함 중 가장 흔한 하나입니다. 필수로 만드는 것이 리뷰를 견디는 유일한 해법입니다. 린트 규칙은 프로젝트가 설치해야 하는 것이고, 기본값 ''은 아무도 알아채지 못하는 것입니다.
절대 화면에 그려지지 않습니다. 독자가 보는 것은 글리프이고, 나머지 전부가 읽는 것은 문장입니다.
모양
아이콘만 있고 라벨이 없는 PlButton은 이미 정사각형이 됩니다. 같은 높이, 같은 너비, 하우스 필렛이 깎인 채로. 이것은 그 다음 모양입니다: 원반.
그 원반은 반경 규칙에 대한 의도된 예외입니다. 그 규칙은 모든 모서리를 컨트롤을 알약으로 만드는 50%에서 한참 못 미치게 붙들어 둡니다. 규칙이 말하는 대상은 라벨이 있는 컨트롤입니다. 위아래 가장자리의 평평한 구간은 글자 한 줄이 앉는 자리인데, 글리프에는 글자 줄이 없습니다. 가운데 표식 하나가 찍힌 원은 성형된 키가 아니라 찍어낸 토큰으로 읽힙니다.
import { PlIconButton } from 'plass-ui';
const Plus = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M12 5v14M5 12h14" />
</svg>
);
export default function IconButtonVariants() {
return (
<div className="flex flex-wrap items-center gap-3">
{(['solid', 'glass', 'ghost'] as const).map((variant) => (
<PlIconButton key={variant} variant={variant} icon={<Plus />} label={`Add (${variant})`} />
))}
</div>
);
}import 'package:flutter/widgets.dart';
import 'package:plass_ui/plass_ui.dart';
import 'package:plass_ui_example/demos/glyphs.dart';
class IconButtonVariants extends StatelessWidget {
const IconButtonVariants({super.key});
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 12,
runSpacing: 12,
crossAxisAlignment: WrapCrossAlignment.center,
children: <Widget>[
for (final PlassVariant variant in PlassVariant.values)
PlIconButton(
variant: variant,
icon: const PlusGlyph(),
label: 'Add (${variant.name})',
onPressed: () {},
),
],
);
}
}size
PlButton과 같은 높이 사다리입니다. 원반과 라벨 버튼을 한 줄에 놓아도 기준선이 유지됩니다. 안쪽 글리프는 독립 아이콘 사다리가 아니라 버튼에 대한 em으로 크기가 정해집니다. 그래서 모든 단계에서 비례가 유지됩니다.
import { PlButton, PlIconButton } from 'plass-ui';
const Plus = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M12 5v14M5 12h14" />
</svg>
);
export default function IconButtonSizes() {
return (
<div className="flex flex-col gap-3">
{(['xs', 'sm', 'md', 'lg', 'xl'] as const).map((size) => (
<div key={size} className="flex items-center gap-3">
<PlIconButton size={size} icon={<Plus />} label={`Add (${size})`} />
<PlButton size={size} variant="glass">
{size}
</PlButton>
</div>
))}
</div>
);
}import 'package:flutter/widgets.dart';
import 'package:plass_ui/plass_ui.dart';
import 'package:plass_ui_example/demos/glyphs.dart';
class IconButtonSizes extends StatelessWidget {
const IconButtonSizes({super.key});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12,
children: <Widget>[
for (final PlassSize size in PlassSize.values)
Row(
mainAxisSize: MainAxisSize.min,
spacing: 12,
children: <Widget>[
PlIconButton(
size: size,
icon: const PlusGlyph(),
label: 'Add (${size.name})',
onPressed: () {},
),
PlButton(
size: size,
variant: PlassVariant.glass,
onPressed: () {},
child: Text(size.name),
),
],
),
],
);
}
}color
import { PlIconButton } from 'plass-ui';
const Star = () => (
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="m12 3 2.6 5.6 6 .8-4.4 4.2 1.1 6.1L12 16.8 6.7 19.7l1.1-6.1L3.4 9.4l6-.8z" />
</svg>
);
export default function IconButtonColors() {
return (
<div className="flex flex-wrap items-center gap-3">
{(['primary', 'secondary', 'success', 'warning', 'danger', 'info'] as const).map((color) => (
<PlIconButton key={color} color={color} icon={<Star />} label={color} />
))}
</div>
);
}import 'package:flutter/widgets.dart';
import 'package:plass_ui/plass_ui.dart';
import 'package:plass_ui_example/demos/glyphs.dart';
class IconButtonColors extends StatelessWidget {
const IconButtonColors({super.key});
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 12,
runSpacing: 12,
crossAxisAlignment: WrapCrossAlignment.center,
children: <Widget>[
for (final PlassColor color in PlassColor.values)
PlIconButton(color: color, icon: const StarGlyph(), label: color.name, onPressed: () {}),
],
);
}
}loading, readOnly, disabled
셋 다 PlButton의 것 그대로입니다. loading은 글리프 자리에 스피너를 놓고 버튼이 실행되지 않게 하되 포커스는 유지합니다. readOnly는 색을 지키고 채도를 뺍니다. disabled는 빛을 끄고 포커스 순서에서 빠집니다.
import { PlIconButton } from 'plass-ui';
const Save = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M5 4h11l3 3v13H5zM8 4v6h8V4M8 20v-6h8v6" />
</svg>
);
export default function IconButtonStates() {
return (
<div className="flex flex-wrap items-center gap-3">
<PlIconButton icon={<Save />} label="Save" />
<PlIconButton icon={<Save />} label="Saving" loading />
<PlIconButton icon={<Save />} label="Saved" readOnly />
<PlIconButton icon={<Save />} label="Unavailable" disabled />
</div>
);
}import 'package:flutter/widgets.dart';
import 'package:plass_ui/plass_ui.dart';
import 'package:plass_ui_example/demos/glyphs.dart';
class IconButtonStates extends StatelessWidget {
const IconButtonStates({super.key});
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 12,
runSpacing: 12,
crossAxisAlignment: WrapCrossAlignment.center,
children: <Widget>[
PlIconButton(icon: const SaveGlyph(), label: 'Save', onPressed: () {}),
PlIconButton(icon: const SaveGlyph(), label: 'Saving', loading: true, onPressed: () {}),
PlIconButton(icon: const SaveGlyph(), label: 'Saved', readOnly: true, onPressed: () {}),
PlIconButton(
icon: const SaveGlyph(),
label: 'Unavailable',
disabled: true,
onPressed: () {},
),
],
);
}
}Accessibility
label이 접근 가능한 이름이고 필수입니다. 여기서 이름을 대신 줄 수 있는 것은 없습니다.- 글리프는 장식입니다. 이미 이름이 있는 컨트롤 안에 있으므로, 그림에서 나온 두 번째 이름은 같은 이름을 두 번 읽는 일이 됩니다.
- 나머지는 전부
PlButton의 것입니다: 포커스 링, 키보드 실행, 로딩 중의aria-busy, 그리고 disabled일 때만 포커스 순서에서 빠지는 것.
- 원반은 여전히 진짜
<button>입니다.render={<a href="…" />}는 그것을 진짜 링크로 만들고, 링크로 안내되며 크롤러가 따라갑니다.
React 빌드와 다른 점
| React | Flutter | 이유 |
|---|---|---|
onClick | onPressed | Flutter의 이름이고, 비워 두는 것이 버튼을 비활성화하는 방법입니다. |
render | — | 바꿔 끼울 요소도, 선언할 링크 시맨틱도 없습니다. |
density, fullWidth | — | density는 좌우 여백을 바꾸는데 아이콘만 있는 버튼에는 그 여백이 없고, 늘어나는 원반은 원반이 아닙니다. |
반경을 위한 인라인 style | PlButton.borderRadius | Flutter에는 인라인 style이 없어서 PlButton이 탈출구를 하나 들고 있고, 그것이 존재하는 이유가 이 위젯입니다. |
className, style | — | 전달할 클래스 목록도 style 속성도 없습니다. |
반경은 넉넉히 큰 수가 아니라 컨트롤 높이의 절반입니다. 상자보다 큰 반경은 그리는 쪽에서 비례로 줄여 버리고, 그렇게 줄여진 원반은 양 끝에서 원반이 아니게 됩니다.