본문으로 건너뛰기

칸반

파인 홈(열)에 작은 카드(지붕 10)가 쌓인 판입니다. 열마다 따로 쌓이고, 열 머리는 상태 글리프 · 이름 · 개수 · 접기를 담습니다. uyoopack 작업 보드에서 끌어올렸습니다.

import
import { Board, BoardColumn, BoardGroup, BoardCard, BoardLine } from "@uyoolabs/ui/board";
쓰는 경우
단계(열)를 따라 흘러가는 작업 · 요청 · 채용 후보처럼 '지금 어디에 있나'가 중요한 것. 열은 7개 이하입니다. 하루 수십 건 쌓이는 열(완료)은 줄 목록(variant="line")과 'n개 더 보기'로 둡니다. 옮길 수 있게 하려면 onMove 를 넘깁니다 — 그때 이동 메뉴가 켜지고, ./board/dnd 를 함께 주면 끌기도 켜집니다.
쓰지 않는 경우
끌기만 있고 이동 메뉴가 없는 보드를 만들지 않습니다(키보드 · 스크린리더 사용자가 옮기지 못합니다). 사람이 아니라 시스템이 열을 정하는 데이터에는 onMove 를 넘기지 않습니다(uyoopack CON-28). 열 색으로 상태를 말하지 않습니다 — 머리의 상태 글리프와 이름이 말합니다. 카드 안에 버튼을 여러 개 두지 않습니다(동작은 ⋯ 메뉴 하나). 들린 카드를 기울이거나 반투명하게 하지 않습니다. 열이 많거나 정렬 · 비교가 중요하면 표를 씁니다.
끌기
import { boardDnd } from "@uyoolabs/ui/board/dnd"; // 끌기를 켤 때만

읽기 전용 — onMove 없음

손잡이 · 이동 버튼 · 이동 메뉴 · 라이브 영역이 DOM 에 없고 카드 전체가 링크 하나입니다. 할 일은 묶음(details, 서버가 그리고 펴고 접는 데 자바스크립트가 들지 않습니다), 진행 중은 한곳, 완료는 줄 목록입니다.

남은 작업 5개 · 진행 중 2개 · 다음: 웹훅 서명 검증을 서버로 옮기기

할 일

3개

진행 중

2개

완료

5개

그 전 완료 32건은 보관 목록에 있습니다.

옮길 수 있는 보드 — onMove + ./board/dnd

카드를 끌어 열 사이 · 열 안 순서를 바꿉니다. 키보드는 카드의 ⋯ 또는 카드 링크에서 Shift+F10 으로 이동 메뉴를 엽니다. 결과는 라이브 영역이 한 · 영 · 일로 읽습니다. 진행 중 열은 WIP 상한 3 입니다.

할 일

3개

진행 중

2개 · 상한 3개

검토

0개

검토를 기다리는 작업이 없습니다. 작업을 마치면 여기로 옮깁니다.

동작

열마다 쌓기
열이 각자 쌓입니다. 묶음 가로줄마다 열을 두는 격자가 아닙니다(한 묶음은 보통 한 열에만 카드가 있어 칸 대부분이 빕니다).
좁은 화면
보드 폭 768 미만에서는 고른 열 하나만 섭니다. 세그먼트(radiogroup)로 바꾸고, 처음 열은 primary 열에 카드가 있으면 그 열, 없으면 첫 열입니다. 서버가 그린 열은 컨테이너 쿼리가 먼저 숨깁니다. 접기는 쓰지 않습니다.
붙는 머리
열 머리는 stickyTop 아래에 붙습니다. 블러 · 반투명 없이 열과 같은 불투명 면이고, 붙으면 아래 헤어라인이 생깁니다.
이동
Board 가 먼저 옮겨 그리고(낙관적) onMove 의 Promise 를 기다립니다. 기다리는 동안 카드는 aria-busy, 거절되면 원래 자리로 돌리고 moveFailed 를 읽습니다. 카드 순서의 정본은 앱 데이터입니다.
끌기
pragmatic-drag-and-drop core 와 auto-scroll 만 씁니다. 터치 · 좁은 화면에서는 같은 열 안 순서만 바꾸고, 열 사이 이동은 메뉴가 맡습니다. 끄는 동안의 위치는 읽지 않고 결과 · 취소 · 실패만 읽습니다. 포인터로 놓은 뒤에는 포커스를 옮기지 않습니다.
WIP 상한
limit 을 주면 머리 개수가 3/5 로 보이고, 넘으면 주의색 글자와 세모 아이콘이 붙습니다. 막지는 않습니다.

키보드

키동작
TabShift+Tab카드 제목 링크 → ⋯ 이동 버튼 → 다음 카드. 열 머리의 접기 버튼
Enter카드를 엽니다(링크)
EnterSpace↓⋯ 에서 이동 메뉴를 엽니다
Shift+F10메뉴 키카드 링크에서 이동 메뉴를 엽니다(onMove 가 있을 때)
↑↓EnterEsc메뉴 안에서 고르기 · 실행 · 닫기. 닫으면 포커스가 카드 링크로, 옮겼으면 새 자리의 카드 링크로 갑니다
←→좁은 화면의 열 전환 세그먼트에서 바로 고릅니다
EnterSpace접힌 열 띠 · 접기 버튼에서 펼치기 · 접기. 포커스는 바뀐 쪽 버튼으로
Esc포인터로 끄는 중이면 취소합니다(원래 자리, moveCancelled)

라이브 영역

보드에 하나(role=status, polite)입니다. onMove 가 없으면 그리지 않습니다.

알림koenja
옮김‘환불 사유 입력 화면’ 카드를 진행 중 3개 중 1번째로 옮겼습니다.Moved “Refund reason form” to In progress, position 1 of 3.「返金理由の入力画面」を進行中の1番目(3件中)に移動しました。
실패 · 되돌림‘환불 사유 입력 화면’ 카드를 옮기지 못해 원래 자리로 돌려놓았습니다.Couldn’t move “Refund reason form”. It’s back in its original place.「返金理由の入力画面」を移動できなかったため、元の位置に戻しました。
끌기 취소‘환불 사유 입력 화면’ 카드 옮기기를 취소했습니다.Cancelled moving “Refund reason form”.「返金理由の入力画面」の移動を取り消しました。

props

소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.

Board

prop타입기본값설명
aria-label필수string—보드 영역의 이름(region). 화면 이름과 같게
activeColumnstring—좁은 화면에서 보일 열(제어). 넓은 화면에서는 모든 열이 보이고 이 값은 쓰지 않는다
defaultActiveColumnstring—비제어 첫 값. 기본: primary 열에 카드가 있으면 그 열, 없으면 첫 열
onActiveColumnChange(columnId: string) => void—
collapsedstring[]—접힌 열 id(제어). 좁은 화면에서는 접기를 쓰지 않는다
defaultCollapsedstring[]—접힌 열 id(비제어 첫 값)
onCollapsedChange(columnIds: string[]) => void—
onMove(move: BoardMove) => void | Promise<void>—주면 카드를 옮길 수 있다. 이동 메뉴(키보드 · 스크린리더)가 켜지고, dnd 를 함께 주면 포인터 끌기도 켜진다. Promise 를 돌려주면 Board 가 먼저 옮겨 그리고(낙관적), 거절되면 원래 자리로 되돌린 뒤 moveFailed 를 읽는다. 앱은 resolve 하기 전에 자기 데이터를 새 순서로 바꾼다. 카드 순서의 정본은 언제나 앱 데이터다
dndBoardDndAdapter—./board/dnd 의 boardDnd. onMove 없이 주면 개발 모드 경고만 내고 끌기를 켜지 않는다
stickyTopnumber | string—열 머리가 붙는 위치. 기본 0. 상단 바 아래면 "var(--uyoo-topbar-h)"
headingLevel2 | 3 | 43열 이름의 제목 수준. 기본 3(화면에 h1 이 없고 섹션 제목이 h2)
toolbarReactNode—툴바 앞에 둘 것(요약 문장 · 필터). 모두 펼치기 · 접기는 묶음이 있으면 Board 가 그린다
nowDate—줄 목록의 상대 시각 기준(테스트 · 스냅샷). 없으면 지금
timeZonestring—줄 목록 시각의 IANA 시간대. 없으면 서울
lang"ko" | "en" | "ja""ko"
labelsPartial<BoardLabels>—
classNamestring—배치용
children필수ReactNode—BoardColumn. 열 안의 카드 · 묶음 · 줄은 감싸지 않고 바로 둔다(Board 가 순서를 읽는다)

BoardColumn

prop타입기본값설명
id필수string—
title필수string—
glyph"todo" | "in-progress" | "done" | "blocked" | "draft" | "pending" | "approved" | "approved-done" | "rejected"—열 머리의 상태 글리프(D-Status). 색이 아니라 모양이 열을 말한다
countnumber—머리의 개수. 기본은 카드 · 줄 수. 서버가 센 전체 수를 넘길 수 있다
limitnumber—WIP 상한. 개수 옆에 3/5 로 보이고 넘으면 주의색이다. 막지는 않는다(결정 #13)
primaryboolean—좁은 화면에서 처음 볼 열 후보(진행 중). 카드가 없으면 첫 열을 본다
variant"card" | "line"—card(기본) · line(완료처럼 수십 건 쌓이는 열). 줄 목록 열은 카드를 옮겨 올 곳이 아니다
visiblenumber—line 에서 처음 보이는 줄 수. 나머지는 접힌 "n개 더 보기"
empty필수ReactNode—필수. 무엇이 없고 어떻게 생기는지 한 문장
footReactNode—열 아래 한 줄(그 전 완료 n건)
collapsibleboolean—좁은 화면에서는 무시한다
childrenReactNode—BoardGroup · BoardCard · BoardLine. 감싸지 않고 바로 둔다

BoardGroup

prop타입기본값설명
id필수string—
title필수string—
numberstring—절 번호(2.1). 제목 앞 보조 색
countnumber—머리의 개수. 기본은 카드 수
defaultOpenboolean—<details open> 의 첫 값. 모두 펼치기 · 접기가 바꾼다
childrenReactNode—BoardCard

BoardCard

prop타입기본값설명
id필수string—
title필수string—
hrefstring—제목 링크. 카드 전체가 이 링크로 눌린다
asChildboolean—앱의 링크를 쓴다. 자식 링크 요소 하나에 제목 · 클래스를 입힌다. <BoardCard asChild …><Link href="…" scroll={false} /></BoardCard>
childrenReactNode—asChild 일 때 링크 요소 하나
itemKeystring—보조 키(PAY-142). mono · muted
badgesReactNode—키 옆 배지(다음 · 우선순위). 5톤 Badge
metaReactNode—맡은 사람 · 마감 · 댓글 수 줄
waitReactNode—먼저 끝나야 하는 작업 문장. 시계 아이콘과 함께
lockedbooleanfalse이 카드만 옮기지 못하게 한다(손잡이 · 이동 메뉴 없음)

BoardLine

prop타입기본값설명
id필수string—
title필수string—
hrefstring—줄 링크. 앱의 링크를 쓰려면 asChild 로 자식 링크 요소 하나를 넘긴다
asChildboolean—
at필수string—UTC ISO. 표시할 때 상대 시각으로 바꾼다
childrenReactNode—asChild 일 때 링크 요소 하나

import

./board 는 radix-ui 가 peer 로 필요합니다(열 전환 세그먼트 · 이동 메뉴). ./board/dnd 는 @atlaskit/pragmatic-drag-and-drop 과 -auto-scroll 이 선택 peer 로 필요하고, 끌기를 켜는 화면만 가져옵니다. 화면은 @uyoolabs/ui/css 에 들어 있습니다.