@reopt-ai/opt-ui 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENT_CATALOG.md +426 -58
- package/README.md +9 -7
- package/dist/core/index.cjs +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +1 -1
- package/dist/docs/02-components/01-core.md +225 -26
- package/dist/docs/02-components/02-visuals.md +1 -1
- package/dist/docs/02-components/03-shells.md +203 -32
- package/dist/docs/02-components/04-surfaces.md +1 -1
- package/dist/docs/02-components/index.md +4 -4
- package/dist/{field-sidebar-Dz7J4ett.d.ts → field-sidebar-CJ4HxP8D.d.cts} +1 -1
- package/dist/{field-sidebar-D-rwuN_w.d.cts → field-sidebar-DnhpcT2A.d.ts} +1 -1
- package/dist/{field-sidebar-CxxSK4JO.cjs → field-sidebar-ePqFX6MN.cjs} +13 -12
- package/dist/{field-sidebar-CRdToHRO.js → field-sidebar-mSVCMAZp.js} +13 -12
- package/dist/id-registry.cjs +8 -0
- package/dist/id-registry.js +8 -0
- package/dist/id-registry.json +17 -0
- package/dist/index.cjs +756 -51
- package/dist/index.d.cts +196 -10
- package/dist/index.d.ts +196 -10
- package/dist/index.js +750 -53
- package/dist/{key-pad-menu-0KWOnELe.cjs → key-pad-menu-BgtSL3xe.cjs} +9 -6
- package/dist/{key-pad-menu-DMmbQ1jF.js → key-pad-menu-CqFI6la9.js} +9 -6
- package/dist/{key-pad-menu-BOVu97bj.d.cts → key-pad-menu-D6pYzaj-.d.cts} +12 -3
- package/dist/{key-pad-menu-NlZ9zNF8.d.ts → key-pad-menu-Yh7Fpb8m.d.ts} +12 -3
- package/dist/meta.cjs +430 -19
- package/dist/meta.js +430 -19
- package/dist/shells/index.cjs +1 -1
- package/dist/shells/index.d.cts +1 -1
- package/dist/shells/index.d.ts +1 -1
- package/dist/shells/index.js +1 -1
- package/dist/{text-truncate-eMzvU_7k.d.cts → text-truncate-Bbul-jMD.d.cts} +1 -30
- package/dist/{text-truncate-eMzvU_7k.d.ts → text-truncate-Bbul-jMD.d.ts} +1 -30
- package/dist/theme/server.d.cts +2 -2
- package/dist/theme/server.d.ts +2 -2
- package/package.json +2 -2
package/dist/meta.js
CHANGED
|
@@ -1221,7 +1221,7 @@ const coreMetas = [
|
|
|
1221
1221
|
{
|
|
1222
1222
|
name: "setOpen",
|
|
1223
1223
|
deprecated: {
|
|
1224
|
-
since: "1.
|
|
1224
|
+
since: "1.13.0",
|
|
1225
1225
|
replacement: "onOpenChange"
|
|
1226
1226
|
},
|
|
1227
1227
|
type: "(open: boolean) => void",
|
|
@@ -1258,6 +1258,26 @@ const coreMetas = [
|
|
|
1258
1258
|
testDescribe: "Disclosure"
|
|
1259
1259
|
},
|
|
1260
1260
|
{
|
|
1261
|
+
usage: {
|
|
1262
|
+
anatomy: ["TabsRoot 안에 TabList와 TabPanel을 두고 Tab.id와 TabPanel.tabId를 맞춥니다."],
|
|
1263
|
+
bestPractices: ["TabList에 접근 가능한 이름을 지정하고, 제어 모드는 selectedId와 onSelectedIdChange를 함께 연결합니다."],
|
|
1264
|
+
avoid: ["다른 URL로 이동하는 주 내비게이션을 탭 패널로 구현하지 않습니다."]
|
|
1265
|
+
},
|
|
1266
|
+
examples: [{
|
|
1267
|
+
validation: "typechecked",
|
|
1268
|
+
title: "이름 있는 두 패널",
|
|
1269
|
+
code: `import { TabsRoot, TabList, Tab, TabPanel } from "@reopt-ai/opt-ui";
|
|
1270
|
+
|
|
1271
|
+
export function Example() {
|
|
1272
|
+
return <TabsRoot defaultSelectedId="overview">
|
|
1273
|
+
<TabList aria-label="프로젝트 정보">
|
|
1274
|
+
<Tab id="overview">개요</Tab><Tab id="activity">활동</Tab>
|
|
1275
|
+
</TabList>
|
|
1276
|
+
<TabPanel tabId="overview">프로젝트 요약</TabPanel>
|
|
1277
|
+
<TabPanel tabId="activity">최근 활동</TabPanel>
|
|
1278
|
+
</TabsRoot>;
|
|
1279
|
+
}`
|
|
1280
|
+
}],
|
|
1261
1281
|
name: "Tabs",
|
|
1262
1282
|
category: "core",
|
|
1263
1283
|
description: "탭 패널 시스템. 4-part anatomy: Root, TabList, Tab, TabPanel.",
|
|
@@ -1302,7 +1322,7 @@ const coreMetas = [
|
|
|
1302
1322
|
{
|
|
1303
1323
|
name: "setSelectedId",
|
|
1304
1324
|
deprecated: {
|
|
1305
|
-
since: "1.
|
|
1325
|
+
since: "1.13.0",
|
|
1306
1326
|
replacement: "onSelectedIdChange"
|
|
1307
1327
|
},
|
|
1308
1328
|
type: "(id: string | null) => void",
|
|
@@ -1385,6 +1405,11 @@ const coreMetas = [
|
|
|
1385
1405
|
testDescribe: "Tabs"
|
|
1386
1406
|
},
|
|
1387
1407
|
{
|
|
1408
|
+
usage: {
|
|
1409
|
+
anatomy: ["SelectRoot 안에 Label, Trigger, Popover, Item을 조합합니다."],
|
|
1410
|
+
bestPractices: ["옵션 value는 표시 문구와 분리된 안정적인 키로 유지합니다. multiple 모드에서는 배열 값을 사용합니다."],
|
|
1411
|
+
avoid: ["선택 목록에 명령 실행 버튼을 섞지 않습니다. 명령은 DropdownMenu로 분리합니다."]
|
|
1412
|
+
},
|
|
1388
1413
|
name: "Select",
|
|
1389
1414
|
category: "core",
|
|
1390
1415
|
description: "드롭다운 선택 컴포넌트. 5-part anatomy: Root, Label, Trigger, Popover, Item.",
|
|
@@ -1474,7 +1499,7 @@ const coreMetas = [
|
|
|
1474
1499
|
{
|
|
1475
1500
|
name: "gutter",
|
|
1476
1501
|
deprecated: {
|
|
1477
|
-
since: "1.
|
|
1502
|
+
since: "1.13.0",
|
|
1478
1503
|
replacement: "sideOffset"
|
|
1479
1504
|
},
|
|
1480
1505
|
type: "number",
|
|
@@ -1525,6 +1550,11 @@ const coreMetas = [
|
|
|
1525
1550
|
}]
|
|
1526
1551
|
},
|
|
1527
1552
|
{
|
|
1553
|
+
usage: {
|
|
1554
|
+
anatomy: ["DialogRoot에서 Disclosure와 Panel을 연결하고 Panel 안에 Heading과 Description을 둡니다."],
|
|
1555
|
+
bestPractices: ["열림을 제어할 때 open과 onOpenChange를 함께 제공합니다. 닫기 동작은 DialogDismiss로 노출합니다."],
|
|
1556
|
+
avoid: ["제목 없는 모달이나 종료 방법 없는 모달을 만들지 않습니다. 파괴적 확인은 AlertDialogPanel의 계약을 사용합니다."]
|
|
1557
|
+
},
|
|
1528
1558
|
name: "Dialog",
|
|
1529
1559
|
category: "core",
|
|
1530
1560
|
description: "모달 다이얼로그. 9-part anatomy: Root, Disclosure, Panel, Heading, Description, Dismiss, Header, Body, Footer.",
|
|
@@ -1556,7 +1586,7 @@ const coreMetas = [
|
|
|
1556
1586
|
}, {
|
|
1557
1587
|
name: "setOpen",
|
|
1558
1588
|
deprecated: {
|
|
1559
|
-
since: "1.
|
|
1589
|
+
since: "1.13.0",
|
|
1560
1590
|
replacement: "onOpenChange"
|
|
1561
1591
|
},
|
|
1562
1592
|
type: "(open: boolean) => void",
|
|
@@ -1683,7 +1713,7 @@ const coreMetas = [
|
|
|
1683
1713
|
ComboboxRoot: [{
|
|
1684
1714
|
name: "setValue",
|
|
1685
1715
|
deprecated: {
|
|
1686
|
-
since: "1.
|
|
1716
|
+
since: "1.13.0",
|
|
1687
1717
|
replacement: "onValueChange"
|
|
1688
1718
|
},
|
|
1689
1719
|
type: "(value: string) => void",
|
|
@@ -1728,7 +1758,7 @@ const coreMetas = [
|
|
|
1728
1758
|
{
|
|
1729
1759
|
name: "gutter",
|
|
1730
1760
|
deprecated: {
|
|
1731
|
-
since: "1.
|
|
1761
|
+
since: "1.13.0",
|
|
1732
1762
|
replacement: "sideOffset"
|
|
1733
1763
|
},
|
|
1734
1764
|
type: "number",
|
|
@@ -1863,7 +1893,7 @@ const coreMetas = [
|
|
|
1863
1893
|
{
|
|
1864
1894
|
name: "gutter",
|
|
1865
1895
|
deprecated: {
|
|
1866
|
-
since: "1.
|
|
1896
|
+
since: "1.13.0",
|
|
1867
1897
|
replacement: "sideOffset"
|
|
1868
1898
|
},
|
|
1869
1899
|
type: "number",
|
|
@@ -2220,7 +2250,7 @@ const coreMetas = [
|
|
|
2220
2250
|
{
|
|
2221
2251
|
name: "gutter",
|
|
2222
2252
|
deprecated: {
|
|
2223
|
-
since: "1.
|
|
2253
|
+
since: "1.13.0",
|
|
2224
2254
|
replacement: "sideOffset"
|
|
2225
2255
|
},
|
|
2226
2256
|
type: "number",
|
|
@@ -2545,6 +2575,10 @@ const store = useFormStore({
|
|
|
2545
2575
|
]
|
|
2546
2576
|
},
|
|
2547
2577
|
{
|
|
2578
|
+
usage: {
|
|
2579
|
+
bestPractices: ["주요 실행에는 primary, 파괴적 실행에는 danger를 사용하고 동작을 설명하는 텍스트를 유지합니다.", "폼 제출은 type=submit을 명시합니다. loading은 버튼을 비활성화하고 aria-busy를 설정합니다."],
|
|
2580
|
+
avoid: ["페이지 이동을 버튼 클릭으로만 구현하지 않습니다. 아이콘만 표시할 때 접근 가능한 이름을 생략하지 않습니다."]
|
|
2581
|
+
},
|
|
2548
2582
|
name: "Button",
|
|
2549
2583
|
category: "core",
|
|
2550
2584
|
description: "접근성 버튼 컴포넌트. 4가지 변형(primary, secondary, ghost, danger), 3가지 크기, 로딩/아이콘과 aria-busy 상태를 지원합니다.",
|
|
@@ -2599,6 +2633,16 @@ const store = useFormStore({
|
|
|
2599
2633
|
}]
|
|
2600
2634
|
},
|
|
2601
2635
|
{
|
|
2636
|
+
usage: {
|
|
2637
|
+
bestPractices: ["variant 색상과 함께 상태를 설명하는 텍스트를 표시합니다. progress를 지정하면 dot 대신 진행률 링이 표시됩니다."],
|
|
2638
|
+
avoid: ["Badge를 클릭 가능한 버튼처럼 사용하거나 색상만으로 성공·실패를 구분하지 않습니다."]
|
|
2639
|
+
},
|
|
2640
|
+
examples: [{
|
|
2641
|
+
validation: "typechecked",
|
|
2642
|
+
title: "텍스트가 있는 상태",
|
|
2643
|
+
code: `import { Badge } from "@reopt-ai/opt-ui";
|
|
2644
|
+
export function Example() { return <Badge variant="success" dot>동기화 완료</Badge>; }`
|
|
2645
|
+
}],
|
|
2602
2646
|
name: "Badge",
|
|
2603
2647
|
category: "core",
|
|
2604
2648
|
description: "인라인 상태 뱃지. 5가지 색상 변형, 상태 점(dot) 옵션 지원.",
|
|
@@ -2638,6 +2682,16 @@ const store = useFormStore({
|
|
|
2638
2682
|
testDescribe: "Badge"
|
|
2639
2683
|
},
|
|
2640
2684
|
{
|
|
2685
|
+
usage: {
|
|
2686
|
+
bestPractices: ["단독 로딩 표시는 aria-label로 작업을 설명합니다. 주변 status가 이미 상태를 읽으면 aria-hidden으로 중복 낭독을 막습니다."],
|
|
2687
|
+
avoid: ["Spinner 자체가 요청 진행 상태나 오류 처리를 관리한다고 가정하지 않습니다."]
|
|
2688
|
+
},
|
|
2689
|
+
examples: [{
|
|
2690
|
+
validation: "typechecked",
|
|
2691
|
+
title: "이름 있는 로딩 표시",
|
|
2692
|
+
code: `import { Spinner } from "@reopt-ai/opt-ui";
|
|
2693
|
+
export function Example() { return <Spinner size="sm" aria-label="검색 결과 불러오는 중" />; }`
|
|
2694
|
+
}],
|
|
2641
2695
|
name: "Spinner",
|
|
2642
2696
|
category: "core",
|
|
2643
2697
|
description: "로딩 상태 표시 스피너. 3가지 크기와 장식용 aria-hidden 모드를 지원합니다.",
|
|
@@ -2653,6 +2707,16 @@ const store = useFormStore({
|
|
|
2653
2707
|
testDescribe: "Spinner"
|
|
2654
2708
|
},
|
|
2655
2709
|
{
|
|
2710
|
+
usage: {
|
|
2711
|
+
bestPractices: ["title에는 문제 요약을, 본문에는 사용자가 할 수 있는 다음 행동을 적습니다. 닫을 수 있다면 onDismiss로 상태를 갱신합니다."],
|
|
2712
|
+
avoid: ["일반 설명 문구를 모두 긴급 알림으로 노출하거나 오류 원인을 색상으로만 전달하지 않습니다."]
|
|
2713
|
+
},
|
|
2714
|
+
examples: [{
|
|
2715
|
+
validation: "typechecked",
|
|
2716
|
+
title: "재시도 가능한 오류 안내",
|
|
2717
|
+
code: `import { Alert } from "@reopt-ai/opt-ui";
|
|
2718
|
+
export function Example() { return <Alert variant="error" title="저장하지 못했습니다">연결 상태를 확인한 뒤 다시 저장하세요.</Alert>; }`
|
|
2719
|
+
}],
|
|
2656
2720
|
name: "Alert",
|
|
2657
2721
|
category: "core",
|
|
2658
2722
|
description: "알림 메시지 컴포넌트. 4가지 변형(info, success, warning, error), 제목/본문/닫기 버튼 지원.",
|
|
@@ -2691,6 +2755,10 @@ const store = useFormStore({
|
|
|
2691
2755
|
testDescribe: "Alert"
|
|
2692
2756
|
},
|
|
2693
2757
|
{
|
|
2758
|
+
usage: {
|
|
2759
|
+
bestPractices: ["label로 입력 목적을, hint로 형식을 안내합니다. error는 오류 문자열이며 표시될 때 hint보다 우선합니다.", "제어 값은 문자열로 유지하고 onChange의 event.target.value를 저장합니다. FormControl 조합에서는 레이블을 중복하지 않습니다."],
|
|
2760
|
+
avoid: ["placeholder를 유일한 레이블로 사용하거나 제어/비제어 상태를 렌더 중 전환하지 않습니다."]
|
|
2761
|
+
},
|
|
2694
2762
|
name: "Input",
|
|
2695
2763
|
category: "core",
|
|
2696
2764
|
description: "독립적인 텍스트 입력 컴포넌트. 레이블, 에러, 힌트, 아이콘 지원.",
|
|
@@ -2757,6 +2825,21 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
2757
2825
|
}]
|
|
2758
2826
|
},
|
|
2759
2827
|
{
|
|
2828
|
+
usage: {
|
|
2829
|
+
bestPractices: ["즉시 적용되는 켜짐/꺼짐 설정에 사용합니다. onChange는 DOM 이벤트가 아니라 boolean을 전달합니다."],
|
|
2830
|
+
avoid: ["제출 시 한꺼번에 적용되는 동의 목록을 즉시 적용 스위치로 표현하지 않습니다. label을 상태마다 바꾸지 않습니다."]
|
|
2831
|
+
},
|
|
2832
|
+
examples: [{
|
|
2833
|
+
validation: "typechecked",
|
|
2834
|
+
title: "제어되는 알림 설정",
|
|
2835
|
+
code: `"use client";
|
|
2836
|
+
import { useState } from "react";
|
|
2837
|
+
import { Switch } from "@reopt-ai/opt-ui";
|
|
2838
|
+
export function Example() {
|
|
2839
|
+
const [enabled, setEnabled] = useState(false);
|
|
2840
|
+
return <Switch label="이메일 알림" hint="새 댓글을 이메일로 받습니다" checked={enabled} onChange={setEnabled} />;
|
|
2841
|
+
}`
|
|
2842
|
+
}],
|
|
2760
2843
|
name: "Switch",
|
|
2761
2844
|
category: "core",
|
|
2762
2845
|
description: "토글 스위치 컴포넌트. 3가지 크기와 레이블/설명의 명시적 접근성 연결을 지원합니다.",
|
|
@@ -2813,6 +2896,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
2813
2896
|
testDescribe: "Switch"
|
|
2814
2897
|
},
|
|
2815
2898
|
{
|
|
2899
|
+
usage: {
|
|
2900
|
+
bestPractices: ["로드될 콘텐츠와 비슷한 크기·행 수를 사용해 레이아웃 이동을 줄입니다. 컨테이너에서 로딩 상태를 설명합니다."],
|
|
2901
|
+
avoid: ["빈 결과나 요청 오류를 끝나지 않는 스켈레톤으로 대신하지 않습니다."]
|
|
2902
|
+
},
|
|
2903
|
+
examples: [{
|
|
2904
|
+
validation: "typechecked",
|
|
2905
|
+
title: "목록 로딩 자리 확보",
|
|
2906
|
+
code: `import { SkeletonTable } from "@reopt-ai/opt-ui";
|
|
2907
|
+
export function Example() { return <section aria-label="목록 불러오는 중" aria-busy="true"><SkeletonTable rows={3} columns={4} /></section>; }`
|
|
2908
|
+
}],
|
|
2816
2909
|
name: "Skeleton",
|
|
2817
2910
|
category: "core",
|
|
2818
2911
|
description: "로딩 플레이스홀더. text/circular/rectangular 변형. 프리셋 컴포넌트 포함.",
|
|
@@ -2883,6 +2976,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
2883
2976
|
testDescribe: "Skeleton"
|
|
2884
2977
|
},
|
|
2885
2978
|
{
|
|
2979
|
+
usage: {
|
|
2980
|
+
bestPractices: ["여러 줄의 자유 입력에 사용하고 label과 hint로 목적·분량을 안내합니다. rows로 초기 높이를 정합니다."],
|
|
2981
|
+
avoid: ["짧은 단일 값 입력에 과도한 편집 영역을 쓰거나 사용자의 세로 크기 조절을 불필요하게 막지 않습니다."]
|
|
2982
|
+
},
|
|
2983
|
+
examples: [{
|
|
2984
|
+
validation: "typechecked",
|
|
2985
|
+
title: "설명 입력",
|
|
2986
|
+
code: `import { Textarea } from "@reopt-ai/opt-ui";
|
|
2987
|
+
export function Example() { return <Textarea label="변경 이유" name="reason" hint="검토자가 알아야 할 내용을 적어주세요" rows={4} resize="vertical" />; }`
|
|
2988
|
+
}],
|
|
2886
2989
|
name: "Textarea",
|
|
2887
2990
|
category: "core",
|
|
2888
2991
|
description: "멀티라인 텍스트 입력. Input과 동일한 API (label, error, hint).",
|
|
@@ -2927,6 +3030,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
2927
3030
|
testDescribe: "Textarea"
|
|
2928
3031
|
},
|
|
2929
3032
|
{
|
|
3033
|
+
usage: {
|
|
3034
|
+
bestPractices: ["name으로 이미지 실패 시 이니셜을 제공하고, alt는 이미지의 실제 의미를 설명합니다. AvatarGroup의 max로 표시 개수를 제한합니다."],
|
|
3035
|
+
avoid: ["이미지나 온라인 상태 점만으로 사용자 신원·상태를 전달하지 않습니다."]
|
|
3036
|
+
},
|
|
3037
|
+
examples: [{
|
|
3038
|
+
validation: "typechecked",
|
|
3039
|
+
title: "이미지 없이 사용자 표시",
|
|
3040
|
+
code: `import { Avatar, AvatarGroup } from "@reopt-ai/opt-ui";
|
|
3041
|
+
export function Example() { return <AvatarGroup max={3}><Avatar name="김민수" /><Avatar name="이지수" /></AvatarGroup>; }`
|
|
3042
|
+
}],
|
|
2930
3043
|
name: "Avatar",
|
|
2931
3044
|
category: "core",
|
|
2932
3045
|
description: "사용자 아바타. 이미지, 이니셜 자동 생성, 스크린 리더에 노출되는 상태 인디케이터, 그룹을 지원합니다.",
|
|
@@ -2990,6 +3103,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
2990
3103
|
testDescribe: "Avatar"
|
|
2991
3104
|
},
|
|
2992
3105
|
{
|
|
3106
|
+
usage: {
|
|
3107
|
+
bestPractices: ["완료량을 알면 value와 max를 같은 단위로 제공합니다. 진행률에 작업을 설명하는 접근 가능한 이름을 붙입니다."],
|
|
3108
|
+
avoid: ["측정할 수 없는 진행률을 임의 숫자로 표시하지 않습니다. 완료·실패 후에도 진행 중 표시를 남기지 않습니다."]
|
|
3109
|
+
},
|
|
3110
|
+
examples: [{
|
|
3111
|
+
validation: "typechecked",
|
|
3112
|
+
title: "업로드 진행률",
|
|
3113
|
+
code: `import { Progress } from "@reopt-ai/opt-ui";
|
|
3114
|
+
export function Example() { return <Progress value={35} max={100} aria-label="파일 업로드" />; }`
|
|
3115
|
+
}],
|
|
2993
3116
|
name: "Progress",
|
|
2994
3117
|
category: "core",
|
|
2995
3118
|
description: "진행률 표시. Linear/Circular, determinate/indeterminate 변형과 안전하게 보정된 ARIA 값을 지원합니다.",
|
|
@@ -3442,6 +3565,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
3442
3565
|
testDescribe: "Card"
|
|
3443
3566
|
},
|
|
3444
3567
|
{
|
|
3568
|
+
usage: {
|
|
3569
|
+
bestPractices: ["CheckboxGroup에는 각 Checkbox의 고유 value를 제공합니다. 단일 onChange는 boolean, 그룹 onChange는 문자열 배열을 전달합니다.", "일부 항목만 선택한 전체 선택 컨트롤에는 indeterminate를 사용합니다."],
|
|
3570
|
+
avoid: ["서로 배타적인 선택지를 체크박스로 구성하거나 label 없이 체크 상태만 노출하지 않습니다."]
|
|
3571
|
+
},
|
|
3572
|
+
examples: [{
|
|
3573
|
+
validation: "typechecked",
|
|
3574
|
+
title: "복수 선택 그룹",
|
|
3575
|
+
code: `import { Checkbox, CheckboxGroup } from "@reopt-ai/opt-ui";
|
|
3576
|
+
export function Example() { return <CheckboxGroup label="알림 채널" defaultValue={["email"]}><Checkbox value="email" label="이메일" /><Checkbox value="push" label="푸시" /></CheckboxGroup>; }`
|
|
3577
|
+
}],
|
|
3445
3578
|
name: "Checkbox",
|
|
3446
3579
|
category: "core",
|
|
3447
3580
|
description: "독립적인 체크박스 컴포넌트. 레이블, 설명, 그룹과 DOM property까지 동기화되는 indeterminate 상태를 지원합니다.",
|
|
@@ -3951,6 +4084,11 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
3951
4084
|
type: "number",
|
|
3952
4085
|
description: "매치 카운트 표시 (toLocaleString 포맷)"
|
|
3953
4086
|
},
|
|
4087
|
+
{
|
|
4088
|
+
name: "labels",
|
|
4089
|
+
type: "ConditionBuilderLabels",
|
|
4090
|
+
description: "미리보기 문구 오버라이드 (previewSuffix, formatPreview). 기본은 영어 \"matches\""
|
|
4091
|
+
},
|
|
3954
4092
|
{
|
|
3955
4093
|
name: "addNestedGroupLabel",
|
|
3956
4094
|
type: "string",
|
|
@@ -3961,6 +4099,17 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
3961
4099
|
testDescribe: "ConditionBuilder"
|
|
3962
4100
|
},
|
|
3963
4101
|
{
|
|
4102
|
+
usage: {
|
|
4103
|
+
anatomy: ["PopoverRoot 안에 Trigger와 Content를 두고 Content 안에 Close를 제공할 수 있습니다."],
|
|
4104
|
+
bestPractices: ["트리거와 관련된 짧은 보조 작업을 배치합니다. 위치는 PopoverRoot의 placement로 지정합니다."],
|
|
4105
|
+
avoid: ["복잡한 필수 입력 작업을 작은 팝오버에 가두거나 트리거의 접근 가능한 이름을 생략하지 않습니다."]
|
|
4106
|
+
},
|
|
4107
|
+
examples: [{
|
|
4108
|
+
validation: "typechecked",
|
|
4109
|
+
title: "보조 설명 열고 닫기",
|
|
4110
|
+
code: `import { PopoverRoot, PopoverTrigger, PopoverContent, PopoverClose } from "@reopt-ai/opt-ui";
|
|
4111
|
+
export function Example() { return <PopoverRoot><PopoverTrigger>공유 안내</PopoverTrigger><PopoverContent><p>초대받은 사용자만 문서를 볼 수 있습니다.</p><PopoverClose>닫기</PopoverClose></PopoverContent></PopoverRoot>; }`
|
|
4112
|
+
}],
|
|
3964
4113
|
name: "Popover",
|
|
3965
4114
|
category: "core",
|
|
3966
4115
|
description: "Popover 프리미티브 래퍼. 트리거 요소에 연결된 플로팅 콘텐츠를 표시합니다.",
|
|
@@ -3979,15 +4128,6 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
3979
4128
|
action: "트리거 활성화"
|
|
3980
4129
|
}],
|
|
3981
4130
|
props: { PopoverContent: [{
|
|
3982
|
-
name: "gutter",
|
|
3983
|
-
deprecated: {
|
|
3984
|
-
since: "1.12.5",
|
|
3985
|
-
replacement: "sideOffset"
|
|
3986
|
-
},
|
|
3987
|
-
type: "number",
|
|
3988
|
-
default: "8",
|
|
3989
|
-
description: "트리거와의 간격 (px)"
|
|
3990
|
-
}, {
|
|
3991
4131
|
name: "className",
|
|
3992
4132
|
type: "string",
|
|
3993
4133
|
description: "커스텀 CSS 클래스"
|
|
@@ -4025,7 +4165,7 @@ const store = useFormStore({ defaultValues: { name: "" } });
|
|
|
4025
4165
|
DropdownContent: [{
|
|
4026
4166
|
name: "gutter",
|
|
4027
4167
|
deprecated: {
|
|
4028
|
-
since: "1.
|
|
4168
|
+
since: "1.13.0",
|
|
4029
4169
|
replacement: "sideOffset"
|
|
4030
4170
|
},
|
|
4031
4171
|
type: "number",
|
|
@@ -4776,6 +4916,16 @@ const MyDashboard = createBlock<MyDashboardProps>(
|
|
|
4776
4916
|
testDescribe: "VirtualList"
|
|
4777
4917
|
},
|
|
4778
4918
|
{
|
|
4919
|
+
usage: {
|
|
4920
|
+
bestPractices: ["기존 콘텐츠를 유지한 채 갱신할 때 loading을 연결하고 label로 진행 중인 작업을 설명합니다."],
|
|
4921
|
+
avoid: ["오버레이가 키보드 접근까지 차단한다고 가정하지 않습니다. 중복 실행 방지가 필요하면 내부 컨트롤도 비활성화합니다."]
|
|
4922
|
+
},
|
|
4923
|
+
examples: [{
|
|
4924
|
+
validation: "typechecked",
|
|
4925
|
+
title: "기존 결과를 유지하는 갱신 표시",
|
|
4926
|
+
code: `import { LoadingOverlay } from "@reopt-ai/opt-ui";
|
|
4927
|
+
export function Example() { return <LoadingOverlay loading label="목록 새로고침 중"><p>이전 조회 결과</p></LoadingOverlay>; }`
|
|
4928
|
+
}],
|
|
4779
4929
|
name: "LoadingOverlay",
|
|
4780
4930
|
category: "core",
|
|
4781
4931
|
description: "로딩 오버레이. 중복 status 없이 하나의 aria-busy 라이브 영역으로 콘텐츠 위 로딩 상태를 표시합니다.",
|
|
@@ -5821,6 +5971,10 @@ const fields = [
|
|
|
5821
5971
|
testDescribe: "DomainTable"
|
|
5822
5972
|
},
|
|
5823
5973
|
{
|
|
5974
|
+
usage: {
|
|
5975
|
+
bestPractices: ["keyExtractor에는 정렬·필터 후에도 유지되는 행 ID를 사용합니다. 보이는 title이 없으면 ariaLabel로 grid 이름을 지정합니다.", "컬럼 accessor는 행 타입에 맞춰 선언하고, 서버 조회·편집·대규모 가상화가 필요하면 opt-datagrid의 계약을 검토합니다."],
|
|
5976
|
+
avoid: ["배열 인덱스를 행 식별자로 사용하거나 비어 있는 헤더로 액션 컬럼을 익명화하지 않습니다. headerHidden을 사용합니다."]
|
|
5977
|
+
},
|
|
5824
5978
|
name: "DataTable",
|
|
5825
5979
|
category: "shell",
|
|
5826
5980
|
description: "제네릭 데이터 테이블. 제목은 opt-in이고 ariaLabel로 grid를 이름 붙이며, 동적 컬럼·안전 정렬·키보드 리사이즈·Composite 탐색을 지원합니다.",
|
|
@@ -5967,6 +6121,17 @@ const data = [
|
|
|
5967
6121
|
}]
|
|
5968
6122
|
},
|
|
5969
6123
|
{
|
|
6124
|
+
usage: {
|
|
6125
|
+
bestPractices: ["앱 경계에 ToastProvider를 한 번 마운트한 뒤 이벤트 핸들러에서 toast를 호출합니다. message는 결과를, action은 다음 행동을 설명합니다."],
|
|
6126
|
+
avoid: ["렌더 함수에서 toast를 호출하거나 반드시 읽어야 하는 오류·동의를 자동으로 사라지는 토스트에만 표시하지 않습니다."]
|
|
6127
|
+
},
|
|
6128
|
+
examples: [{
|
|
6129
|
+
validation: "typechecked",
|
|
6130
|
+
title: "사용자 동작의 결과 알림",
|
|
6131
|
+
code: `"use client";
|
|
6132
|
+
import { Button, ToastProvider, toast } from "@reopt-ai/opt-ui";
|
|
6133
|
+
export function Example() { return <ToastProvider><Button onClick={() => toast.success("알림 동작을 확인했습니다")}>알림 테스트</Button></ToastProvider>; }`
|
|
6134
|
+
}],
|
|
5970
6135
|
name: "NotificationToast",
|
|
5971
6136
|
category: "shell",
|
|
5972
6137
|
description: "토스트 알림 시스템. 포인터/키보드 상호작용 중 자동 닫힘을 일시 정지하고, 중요도별 live region으로 전역 알림을 관리합니다.",
|
|
@@ -6161,6 +6326,10 @@ const data = [
|
|
|
6161
6326
|
testDescribe: "ActivityFeed"
|
|
6162
6327
|
},
|
|
6163
6328
|
{
|
|
6329
|
+
usage: {
|
|
6330
|
+
bestPractices: ["filters의 id를 안정적으로 유지하고 onFilterChange에서 해당 필터의 value를 갱신합니다. onClearAll에서는 적용된 값과 조회 조건을 함께 초기화합니다."],
|
|
6331
|
+
avoid: ["필터 UI 변경만으로 서버 데이터까지 자동 조회된다고 가정하지 않습니다. 비동기 조회와 오류 처리는 호출자가 연결합니다."]
|
|
6332
|
+
},
|
|
6164
6333
|
name: "FilterBar",
|
|
6165
6334
|
category: "shell",
|
|
6166
6335
|
description: "필터링 바 컴포넌트. 검색, 선택, 다중 선택 필터 지원.",
|
|
@@ -7375,6 +7544,221 @@ const filters = [
|
|
|
7375
7544
|
}],
|
|
7376
7545
|
testDescribe: "SegmentBuilder"
|
|
7377
7546
|
},
|
|
7547
|
+
{
|
|
7548
|
+
name: "CriteriaBuilder",
|
|
7549
|
+
category: "shell",
|
|
7550
|
+
description: "도메인 무관 2단계 조건 트리 편집기. 그룹 AND/OR, 순서·복제·부정·오류를 셸이 맡고 조건 문장은 소비자가 Expression 칩으로 렌더링합니다.",
|
|
7551
|
+
slug: "criteria-builder",
|
|
7552
|
+
exports: [
|
|
7553
|
+
"CriteriaBuilder",
|
|
7554
|
+
"criteriaBuilderMove",
|
|
7555
|
+
"criteriaBuilderDuplicate",
|
|
7556
|
+
"criteriaBuilderRemove",
|
|
7557
|
+
"criteriaBuilderInsert",
|
|
7558
|
+
"criteriaBuilderRemoveGroup",
|
|
7559
|
+
"criteriaBuilderDuplicateGroup",
|
|
7560
|
+
"criteriaGroupLetter"
|
|
7561
|
+
],
|
|
7562
|
+
props: { CriteriaBuilder: [
|
|
7563
|
+
{
|
|
7564
|
+
name: "value",
|
|
7565
|
+
type: "CriteriaBuilderValue<TCriterion>",
|
|
7566
|
+
required: true,
|
|
7567
|
+
description: "조건 트리. { logic, groups: [{ id, logic, criteria }] }"
|
|
7568
|
+
},
|
|
7569
|
+
{
|
|
7570
|
+
name: "onChange",
|
|
7571
|
+
type: "(next: CriteriaBuilderValue<TCriterion>) => void",
|
|
7572
|
+
required: true,
|
|
7573
|
+
description: "트리 변경 핸들러"
|
|
7574
|
+
},
|
|
7575
|
+
{
|
|
7576
|
+
name: "kinds",
|
|
7577
|
+
type: "CriteriaBuilderKindOption[]",
|
|
7578
|
+
required: true,
|
|
7579
|
+
description: "조건 추가 메뉴 항목. group으로 섹션을 나누고 negatedLabel이 있으면 부정 항목을 따로 냅니다"
|
|
7580
|
+
},
|
|
7581
|
+
{
|
|
7582
|
+
name: "createCriterion",
|
|
7583
|
+
type: "(kind: string, negate: boolean) => TCriterion",
|
|
7584
|
+
required: true,
|
|
7585
|
+
description: "메뉴 선택으로 만들 초기 조건. 고유 id를 돌려줘야 합니다"
|
|
7586
|
+
},
|
|
7587
|
+
{
|
|
7588
|
+
name: "renderCriterion",
|
|
7589
|
+
type: "(criterion: TCriterion, api: CriteriaRenderApi<TCriterion>) => React.ReactNode",
|
|
7590
|
+
required: true,
|
|
7591
|
+
description: "조건 한 줄의 편집 가능한 문장 렌더러"
|
|
7592
|
+
},
|
|
7593
|
+
{
|
|
7594
|
+
name: "cloneCriterion",
|
|
7595
|
+
type: "(criterion: TCriterion) => TCriterion",
|
|
7596
|
+
description: "복제 시 만들 사본. 기본은 새 id를 붙인 얕은 복사"
|
|
7597
|
+
},
|
|
7598
|
+
{
|
|
7599
|
+
name: "createGroupId",
|
|
7600
|
+
type: "() => string",
|
|
7601
|
+
description: "셸이 만드는 그룹의 id. 기본 group-N"
|
|
7602
|
+
},
|
|
7603
|
+
{
|
|
7604
|
+
name: "errors",
|
|
7605
|
+
type: "Record<string, string>",
|
|
7606
|
+
description: "조건 id 또는 그룹 id별 오류 메시지"
|
|
7607
|
+
},
|
|
7608
|
+
{
|
|
7609
|
+
name: "maxGroups",
|
|
7610
|
+
type: "number",
|
|
7611
|
+
default: "10",
|
|
7612
|
+
description: "그룹 최대 수"
|
|
7613
|
+
},
|
|
7614
|
+
{
|
|
7615
|
+
name: "maxCriteriaPerGroup",
|
|
7616
|
+
type: "number",
|
|
7617
|
+
default: "20",
|
|
7618
|
+
description: "그룹당 조건 최대 수"
|
|
7619
|
+
},
|
|
7620
|
+
{
|
|
7621
|
+
name: "allowNegation",
|
|
7622
|
+
type: "boolean",
|
|
7623
|
+
default: "true",
|
|
7624
|
+
description: "조건별 부정 토글 표시"
|
|
7625
|
+
},
|
|
7626
|
+
{
|
|
7627
|
+
name: "disabled",
|
|
7628
|
+
type: "boolean",
|
|
7629
|
+
default: "false",
|
|
7630
|
+
description: "전체 비활성화"
|
|
7631
|
+
},
|
|
7632
|
+
{
|
|
7633
|
+
name: "aside",
|
|
7634
|
+
type: "React.ReactNode",
|
|
7635
|
+
description: "넓은 화면에서 트리 옆, 좁은 화면에서 아래에 놓이는 미리보기 슬롯"
|
|
7636
|
+
},
|
|
7637
|
+
{
|
|
7638
|
+
name: "labels",
|
|
7639
|
+
type: "CriteriaBuilderLabels",
|
|
7640
|
+
description: "i18n용 라벨 오버라이드 (기본 한국어)"
|
|
7641
|
+
},
|
|
7642
|
+
{
|
|
7643
|
+
name: "className",
|
|
7644
|
+
type: "string",
|
|
7645
|
+
description: "루트 CSS 클래스"
|
|
7646
|
+
},
|
|
7647
|
+
{
|
|
7648
|
+
name: "aria-label",
|
|
7649
|
+
type: "string",
|
|
7650
|
+
description: "빌더 접근성 이름"
|
|
7651
|
+
}
|
|
7652
|
+
] },
|
|
7653
|
+
ariaRole: "group",
|
|
7654
|
+
keyboardShortcuts: [{
|
|
7655
|
+
keys: "A",
|
|
7656
|
+
action: "포커스된 그룹에 조건 추가 메뉴 열기"
|
|
7657
|
+
}, {
|
|
7658
|
+
keys: "G",
|
|
7659
|
+
action: "그룹 추가"
|
|
7660
|
+
}],
|
|
7661
|
+
dependencies: [
|
|
7662
|
+
{
|
|
7663
|
+
slug: "segmented-control",
|
|
7664
|
+
category: "core"
|
|
7665
|
+
},
|
|
7666
|
+
{
|
|
7667
|
+
slug: "dropdown-menu",
|
|
7668
|
+
category: "core"
|
|
7669
|
+
},
|
|
7670
|
+
{
|
|
7671
|
+
slug: "button",
|
|
7672
|
+
category: "core"
|
|
7673
|
+
},
|
|
7674
|
+
{
|
|
7675
|
+
slug: "callout",
|
|
7676
|
+
category: "core"
|
|
7677
|
+
},
|
|
7678
|
+
{
|
|
7679
|
+
slug: "empty-state",
|
|
7680
|
+
category: "core"
|
|
7681
|
+
},
|
|
7682
|
+
{
|
|
7683
|
+
slug: "kbd",
|
|
7684
|
+
category: "core"
|
|
7685
|
+
}
|
|
7686
|
+
],
|
|
7687
|
+
testDescribe: "CriteriaBuilder",
|
|
7688
|
+
usage: {
|
|
7689
|
+
bestPractices: [
|
|
7690
|
+
"조건 데이터 타입과 각 조건의 문장은 소비자가 소유합니다. renderCriterion에서 Expression 칩을 조합하고, 값 편집은 Popover로 여세요.",
|
|
7691
|
+
"createCriterion은 kind와 negate만 받아 초기 조건을 만듭니다. id는 여기서 고유하게 발급하세요.",
|
|
7692
|
+
"errors는 조건 id·그룹 id로 키를 잡습니다. 검증은 소비자가 하고 셸은 표시와 aria 연결만 맡습니다.",
|
|
7693
|
+
"aside에 라이브 카운트(StatCard)를 두면 넓은 화면에서 트리 옆에, 좁은 화면에서 아래에 놓입니다."
|
|
7694
|
+
],
|
|
7695
|
+
anatomy: ["루트 AND/OR 세그먼트 컨트롤 → 그룹 패널(레터마크 + 그룹 AND/OR + 복제/제거) → 조건 행(부정 토글 + 문장 + 이동/복제/제거) → 조건 추가 메뉴 → 그룹 추가 버튼"],
|
|
7696
|
+
avoid: ["kinds에 없는 kind를 다른 kind의 필드로 대체 렌더링하지 마세요. 셸은 이를 경고 행으로 보여 주고 바꾸기/제거만 허용합니다.", "SegmentBuilder처럼 고정 select 조합이 충분하다면 이 셸은 과합니다. 문장형 편집과 그룹 논리가 필요할 때 쓰세요."]
|
|
7697
|
+
},
|
|
7698
|
+
examples: [{
|
|
7699
|
+
validation: "typechecked",
|
|
7700
|
+
title: "이벤트 조건 세그먼트",
|
|
7701
|
+
code: `"use client";
|
|
7702
|
+
import { useState } from "react";
|
|
7703
|
+
import {
|
|
7704
|
+
CriteriaBuilder,
|
|
7705
|
+
Expression,
|
|
7706
|
+
ExpressionGroup,
|
|
7707
|
+
type CriteriaBuilderValue,
|
|
7708
|
+
} from "@reopt-ai/opt-ui";
|
|
7709
|
+
|
|
7710
|
+
interface Criterion {
|
|
7711
|
+
id: string;
|
|
7712
|
+
kind: string;
|
|
7713
|
+
negate?: boolean;
|
|
7714
|
+
event: string;
|
|
7715
|
+
}
|
|
7716
|
+
|
|
7717
|
+
let seq = 0;
|
|
7718
|
+
|
|
7719
|
+
export function Example() {
|
|
7720
|
+
const [value, setValue] = useState<CriteriaBuilderValue<Criterion>>({
|
|
7721
|
+
logic: "and",
|
|
7722
|
+
groups: [
|
|
7723
|
+
{
|
|
7724
|
+
id: "g1",
|
|
7725
|
+
logic: "and",
|
|
7726
|
+
criteria: [{ id: "c1", kind: "performed", event: "purchase" }],
|
|
7727
|
+
},
|
|
7728
|
+
],
|
|
7729
|
+
});
|
|
7730
|
+
return (
|
|
7731
|
+
<CriteriaBuilder
|
|
7732
|
+
value={value}
|
|
7733
|
+
onChange={setValue}
|
|
7734
|
+
kinds={[
|
|
7735
|
+
{
|
|
7736
|
+
kind: "performed",
|
|
7737
|
+
label: "이벤트를 수행함",
|
|
7738
|
+
negatedLabel: "이벤트를 수행하지 않음",
|
|
7739
|
+
},
|
|
7740
|
+
]}
|
|
7741
|
+
createCriterion={(kind, negate) => ({
|
|
7742
|
+
id: \`c\${++seq}\`,
|
|
7743
|
+
kind,
|
|
7744
|
+
negate,
|
|
7745
|
+
event: "",
|
|
7746
|
+
})}
|
|
7747
|
+
renderCriterion={(criterion, api) => (
|
|
7748
|
+
<ExpressionGroup>
|
|
7749
|
+
<Expression
|
|
7750
|
+
description={criterion.negate ? "수행하지 않음" : "수행함"}
|
|
7751
|
+
value={criterion.event || "이벤트 선택"}
|
|
7752
|
+
invalid={api.invalid || criterion.event === ""}
|
|
7753
|
+
onClick={() => api.update({ event: "purchase" })}
|
|
7754
|
+
/>
|
|
7755
|
+
</ExpressionGroup>
|
|
7756
|
+
)}
|
|
7757
|
+
/>
|
|
7758
|
+
);
|
|
7759
|
+
}`
|
|
7760
|
+
}]
|
|
7761
|
+
},
|
|
7378
7762
|
{
|
|
7379
7763
|
name: "InsightsPanel",
|
|
7380
7764
|
category: "shell",
|
|
@@ -7431,6 +7815,21 @@ const filters = [
|
|
|
7431
7815
|
testDescribe: "InsightsPanel"
|
|
7432
7816
|
},
|
|
7433
7817
|
{
|
|
7818
|
+
usage: {
|
|
7819
|
+
bestPractices: ["제어 모드에서는 value와 onChange를 함께 연결합니다. labels와 presets의 label은 제품 언어로 제공합니다.", "프리셋은 로컬 날짜의 자정을 기준으로 계산하므로 API의 시간대·종료일 포함 여부를 조회 계층에서 명시적으로 변환합니다."],
|
|
7820
|
+
avoid: ["날짜 선택만으로 서버 집계가 갱신된다고 가정하거나 null 날짜를 유효한 조회 범위로 전송하지 않습니다."]
|
|
7821
|
+
},
|
|
7822
|
+
examples: [{
|
|
7823
|
+
validation: "typechecked",
|
|
7824
|
+
title: "제어되는 조회 기간",
|
|
7825
|
+
code: `"use client";
|
|
7826
|
+
import { useState } from "react";
|
|
7827
|
+
import { TimeRangeSelector, type DateRange } from "@reopt-ai/opt-ui";
|
|
7828
|
+
export function Example() {
|
|
7829
|
+
const [range, setRange] = useState<DateRange>({ start: null, end: null });
|
|
7830
|
+
return <TimeRangeSelector value={range} onChange={setRange} labels={{ title: "조회 기간", placeholder: "기간 선택" }} presets={[{ label: "최근 7일", days: 7 }]} />;
|
|
7831
|
+
}`
|
|
7832
|
+
}],
|
|
7434
7833
|
name: "TimeRangeSelector",
|
|
7435
7834
|
category: "shell",
|
|
7436
7835
|
description: "시간 범위 선택기. Popover + DateRangePicker + 프리셋 버튼 조합. 12개 시계열 Surface에서 사용.",
|
|
@@ -7520,6 +7919,19 @@ const filters = [
|
|
|
7520
7919
|
testDescribe: "ExportButton"
|
|
7521
7920
|
},
|
|
7522
7921
|
{
|
|
7922
|
+
usage: {
|
|
7923
|
+
bestPractices: ["stats에는 안정적인 id와 표시 value를 제공합니다. 감소가 좋은 지표는 polarity=negative로 비교 의미를 명시합니다.", "선택 가능한 카드에서는 onStatClick과 selectedStatId를 같은 조회 상태에 연결합니다. 빈 목록 문구는 labels로 제공합니다."],
|
|
7924
|
+
avoid: ["loading 중 빈 결과를 확정적으로 표시하거나 모든 상승 추세를 좋은 결과로 해석하지 않습니다."]
|
|
7925
|
+
},
|
|
7926
|
+
examples: [{
|
|
7927
|
+
validation: "typechecked",
|
|
7928
|
+
title: "의미가 다른 통계 비교",
|
|
7929
|
+
code: `import { SummaryRow } from "@reopt-ai/opt-ui";
|
|
7930
|
+
export function Example() { return <SummaryRow columns={2} stats={[
|
|
7931
|
+
{ id: "orders", title: "주문", value: "120", change: "+10%", trend: "up" },
|
|
7932
|
+
{ id: "errors", title: "오류", value: "2", change: "-50%", trend: "down", polarity: "negative" },
|
|
7933
|
+
]} />; }`
|
|
7934
|
+
}],
|
|
7523
7935
|
name: "SummaryRow",
|
|
7524
7936
|
category: "shell",
|
|
7525
7937
|
description: "통계 요약 행. StatCard 그리드 레이아웃을 표준화합니다. loading 시 SkeletonCard 표시.",
|
|
@@ -10682,7 +11094,6 @@ const messages = [
|
|
|
10682
11094
|
category: "core"
|
|
10683
11095
|
}
|
|
10684
11096
|
],
|
|
10685
|
-
ariaRole: "dialog",
|
|
10686
11097
|
testDescribe: "SaveQueryDialog"
|
|
10687
11098
|
},
|
|
10688
11099
|
{
|
package/dist/shells/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use client";
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
-
const require_field_sidebar = require("../field-sidebar-
|
|
4
|
+
const require_field_sidebar = require("../field-sidebar-ePqFX6MN.cjs");
|
|
5
5
|
let _reopt_ai_opt_charts = require("@reopt-ai/opt-charts");
|
|
6
6
|
exports.AppMenubar = require_field_sidebar.AppMenubar;
|
|
7
7
|
exports.BranchSelect = require_field_sidebar.BranchSelect;
|