@reopt-ai/opt-ui 1.12.5 → 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.
Files changed (42) hide show
  1. package/CHANGELOG.md +236 -0
  2. package/COMPONENT_CATALOG.md +670 -123
  3. package/README.md +9 -7
  4. package/dist/core/index.cjs +13 -13
  5. package/dist/core/index.d.cts +2 -2
  6. package/dist/core/index.d.ts +2 -2
  7. package/dist/core/index.js +2 -2
  8. package/dist/docs/02-components/01-core.md +260 -61
  9. package/dist/docs/02-components/02-visuals.md +1 -1
  10. package/dist/docs/02-components/03-shells.md +431 -63
  11. package/dist/docs/02-components/04-surfaces.md +1 -1
  12. package/dist/docs/02-components/index.md +4 -4
  13. package/dist/{field-sidebar-YaWHfLLW.d.ts → field-sidebar-CJ4HxP8D.d.cts} +824 -2
  14. package/dist/{field-sidebar-Ct8c9L4V.d.cts → field-sidebar-DnhpcT2A.d.ts} +824 -2
  15. package/dist/{field-sidebar-DwtrYSSt.cjs → field-sidebar-ePqFX6MN.cjs} +4339 -244
  16. package/dist/{field-sidebar-Ben7MQD4.js → field-sidebar-mSVCMAZp.js} +3976 -235
  17. package/dist/id-registry.cjs +80 -0
  18. package/dist/id-registry.js +80 -0
  19. package/dist/id-registry.json +170 -0
  20. package/dist/index.cjs +1096 -1476
  21. package/dist/index.d.cts +243 -92
  22. package/dist/index.d.ts +243 -92
  23. package/dist/index.js +1004 -1415
  24. package/dist/{key-pad-menu-BpQebDyw.cjs → key-pad-menu-BgtSL3xe.cjs} +60 -487
  25. package/dist/{key-pad-menu-CYYtS_Tl.js → key-pad-menu-CqFI6la9.js} +61 -422
  26. package/dist/{key-pad-menu-CwcY_Vps.d.cts → key-pad-menu-D6pYzaj-.d.cts} +14 -56
  27. package/dist/{key-pad-menu-WBJShCJn.d.ts → key-pad-menu-Yh7Fpb8m.d.ts} +14 -56
  28. package/dist/meta.cjs +1312 -12
  29. package/dist/meta.js +1312 -12
  30. package/dist/shells/index.cjs +20 -1
  31. package/dist/shells/index.d.cts +2 -2
  32. package/dist/shells/index.d.ts +2 -2
  33. package/dist/shells/index.js +2 -2
  34. package/dist/{text-truncate-CQyfUZvq.d.cts → text-truncate-Bbul-jMD.d.cts} +52 -30
  35. package/dist/{text-truncate-CQyfUZvq.d.ts → text-truncate-Bbul-jMD.d.ts} +52 -30
  36. package/dist/{text-truncate-Dlm7JwDf.js → text-truncate-Bd-0WkSP.js} +461 -97
  37. package/dist/{text-truncate-CqdiQuj8.cjs → text-truncate-BeYW6XZu.cjs} +523 -99
  38. package/dist/theme/server.cjs +2 -2
  39. package/dist/theme/server.d.cts +2 -2
  40. package/dist/theme/server.d.ts +2 -2
  41. package/dist/theme/server.js +2 -2
  42. package/package.json +6 -2
package/dist/meta.js CHANGED
@@ -1220,6 +1220,10 @@ const coreMetas = [
1220
1220
  },
1221
1221
  {
1222
1222
  name: "setOpen",
1223
+ deprecated: {
1224
+ since: "1.13.0",
1225
+ replacement: "onOpenChange"
1226
+ },
1223
1227
  type: "(open: boolean) => void",
1224
1228
  description: "제어 모드: 상태 변경 핸들러"
1225
1229
  },
@@ -1254,6 +1258,26 @@ const coreMetas = [
1254
1258
  testDescribe: "Disclosure"
1255
1259
  },
1256
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
+ }],
1257
1281
  name: "Tabs",
1258
1282
  category: "core",
1259
1283
  description: "탭 패널 시스템. 4-part anatomy: Root, TabList, Tab, TabPanel.",
@@ -1297,6 +1321,10 @@ const coreMetas = [
1297
1321
  },
1298
1322
  {
1299
1323
  name: "setSelectedId",
1324
+ deprecated: {
1325
+ since: "1.13.0",
1326
+ replacement: "onSelectedIdChange"
1327
+ },
1300
1328
  type: "(id: string | null) => void",
1301
1329
  description: "제어 모드: 탭 변경 핸들러"
1302
1330
  }
@@ -1377,6 +1405,11 @@ const coreMetas = [
1377
1405
  testDescribe: "Tabs"
1378
1406
  },
1379
1407
  {
1408
+ usage: {
1409
+ anatomy: ["SelectRoot 안에 Label, Trigger, Popover, Item을 조합합니다."],
1410
+ bestPractices: ["옵션 value는 표시 문구와 분리된 안정적인 키로 유지합니다. multiple 모드에서는 배열 값을 사용합니다."],
1411
+ avoid: ["선택 목록에 명령 실행 버튼을 섞지 않습니다. 명령은 DropdownMenu로 분리합니다."]
1412
+ },
1380
1413
  name: "Select",
1381
1414
  category: "core",
1382
1415
  description: "드롭다운 선택 컴포넌트. 5-part anatomy: Root, Label, Trigger, Popover, Item.",
@@ -1465,6 +1498,10 @@ const coreMetas = [
1465
1498
  },
1466
1499
  {
1467
1500
  name: "gutter",
1501
+ deprecated: {
1502
+ since: "1.13.0",
1503
+ replacement: "sideOffset"
1504
+ },
1468
1505
  type: "number",
1469
1506
  default: "4",
1470
1507
  description: "트리거와의 간격 (px)"
@@ -1513,6 +1550,11 @@ const coreMetas = [
1513
1550
  }]
1514
1551
  },
1515
1552
  {
1553
+ usage: {
1554
+ anatomy: ["DialogRoot에서 Disclosure와 Panel을 연결하고 Panel 안에 Heading과 Description을 둡니다."],
1555
+ bestPractices: ["열림을 제어할 때 open과 onOpenChange를 함께 제공합니다. 닫기 동작은 DialogDismiss로 노출합니다."],
1556
+ avoid: ["제목 없는 모달이나 종료 방법 없는 모달을 만들지 않습니다. 파괴적 확인은 AlertDialogPanel의 계약을 사용합니다."]
1557
+ },
1516
1558
  name: "Dialog",
1517
1559
  category: "core",
1518
1560
  description: "모달 다이얼로그. 9-part anatomy: Root, Disclosure, Panel, Heading, Description, Dismiss, Header, Body, Footer.",
@@ -1543,6 +1585,10 @@ const coreMetas = [
1543
1585
  description: "제어 모드: 열림 상태"
1544
1586
  }, {
1545
1587
  name: "setOpen",
1588
+ deprecated: {
1589
+ since: "1.13.0",
1590
+ replacement: "onOpenChange"
1591
+ },
1546
1592
  type: "(open: boolean) => void",
1547
1593
  description: "제어 모드: 상태 변경 핸들러"
1548
1594
  }],
@@ -1666,6 +1712,10 @@ const coreMetas = [
1666
1712
  props: {
1667
1713
  ComboboxRoot: [{
1668
1714
  name: "setValue",
1715
+ deprecated: {
1716
+ since: "1.13.0",
1717
+ replacement: "onValueChange"
1718
+ },
1669
1719
  type: "(value: string) => void",
1670
1720
  description: "입력값 변경 핸들러"
1671
1721
  }, {
@@ -1707,6 +1757,10 @@ const coreMetas = [
1707
1757
  },
1708
1758
  {
1709
1759
  name: "gutter",
1760
+ deprecated: {
1761
+ since: "1.13.0",
1762
+ replacement: "sideOffset"
1763
+ },
1710
1764
  type: "number",
1711
1765
  default: "4",
1712
1766
  description: "입력과의 간격 (px)"
@@ -1838,6 +1892,10 @@ const coreMetas = [
1838
1892
  },
1839
1893
  {
1840
1894
  name: "gutter",
1895
+ deprecated: {
1896
+ since: "1.13.0",
1897
+ replacement: "sideOffset"
1898
+ },
1841
1899
  type: "number",
1842
1900
  default: "8",
1843
1901
  description: "트리거와의 간격 (px)"
@@ -2191,6 +2249,10 @@ const coreMetas = [
2191
2249
  },
2192
2250
  {
2193
2251
  name: "gutter",
2252
+ deprecated: {
2253
+ since: "1.13.0",
2254
+ replacement: "sideOffset"
2255
+ },
2194
2256
  type: "number",
2195
2257
  default: "4",
2196
2258
  description: "트리거와의 간격 (px)"
@@ -2513,6 +2575,10 @@ const store = useFormStore({
2513
2575
  ]
2514
2576
  },
2515
2577
  {
2578
+ usage: {
2579
+ bestPractices: ["주요 실행에는 primary, 파괴적 실행에는 danger를 사용하고 동작을 설명하는 텍스트를 유지합니다.", "폼 제출은 type=submit을 명시합니다. loading은 버튼을 비활성화하고 aria-busy를 설정합니다."],
2580
+ avoid: ["페이지 이동을 버튼 클릭으로만 구현하지 않습니다. 아이콘만 표시할 때 접근 가능한 이름을 생략하지 않습니다."]
2581
+ },
2516
2582
  name: "Button",
2517
2583
  category: "core",
2518
2584
  description: "접근성 버튼 컴포넌트. 4가지 변형(primary, secondary, ghost, danger), 3가지 크기, 로딩/아이콘과 aria-busy 상태를 지원합니다.",
@@ -2567,6 +2633,16 @@ const store = useFormStore({
2567
2633
  }]
2568
2634
  },
2569
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
+ }],
2570
2646
  name: "Badge",
2571
2647
  category: "core",
2572
2648
  description: "인라인 상태 뱃지. 5가지 색상 변형, 상태 점(dot) 옵션 지원.",
@@ -2606,6 +2682,16 @@ const store = useFormStore({
2606
2682
  testDescribe: "Badge"
2607
2683
  },
2608
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
+ }],
2609
2695
  name: "Spinner",
2610
2696
  category: "core",
2611
2697
  description: "로딩 상태 표시 스피너. 3가지 크기와 장식용 aria-hidden 모드를 지원합니다.",
@@ -2621,6 +2707,16 @@ const store = useFormStore({
2621
2707
  testDescribe: "Spinner"
2622
2708
  },
2623
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
+ }],
2624
2720
  name: "Alert",
2625
2721
  category: "core",
2626
2722
  description: "알림 메시지 컴포넌트. 4가지 변형(info, success, warning, error), 제목/본문/닫기 버튼 지원.",
@@ -2659,6 +2755,10 @@ const store = useFormStore({
2659
2755
  testDescribe: "Alert"
2660
2756
  },
2661
2757
  {
2758
+ usage: {
2759
+ bestPractices: ["label로 입력 목적을, hint로 형식을 안내합니다. error는 오류 문자열이며 표시될 때 hint보다 우선합니다.", "제어 값은 문자열로 유지하고 onChange의 event.target.value를 저장합니다. FormControl 조합에서는 레이블을 중복하지 않습니다."],
2760
+ avoid: ["placeholder를 유일한 레이블로 사용하거나 제어/비제어 상태를 렌더 중 전환하지 않습니다."]
2761
+ },
2662
2762
  name: "Input",
2663
2763
  category: "core",
2664
2764
  description: "독립적인 텍스트 입력 컴포넌트. 레이블, 에러, 힌트, 아이콘 지원.",
@@ -2725,6 +2825,21 @@ const store = useFormStore({ defaultValues: { name: "" } });
2725
2825
  }]
2726
2826
  },
2727
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
+ }],
2728
2843
  name: "Switch",
2729
2844
  category: "core",
2730
2845
  description: "토글 스위치 컴포넌트. 3가지 크기와 레이블/설명의 명시적 접근성 연결을 지원합니다.",
@@ -2781,6 +2896,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
2781
2896
  testDescribe: "Switch"
2782
2897
  },
2783
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
+ }],
2784
2909
  name: "Skeleton",
2785
2910
  category: "core",
2786
2911
  description: "로딩 플레이스홀더. text/circular/rectangular 변형. 프리셋 컴포넌트 포함.",
@@ -2851,6 +2976,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
2851
2976
  testDescribe: "Skeleton"
2852
2977
  },
2853
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
+ }],
2854
2989
  name: "Textarea",
2855
2990
  category: "core",
2856
2991
  description: "멀티라인 텍스트 입력. Input과 동일한 API (label, error, hint).",
@@ -2895,6 +3030,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
2895
3030
  testDescribe: "Textarea"
2896
3031
  },
2897
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
+ }],
2898
3043
  name: "Avatar",
2899
3044
  category: "core",
2900
3045
  description: "사용자 아바타. 이미지, 이니셜 자동 생성, 스크린 리더에 노출되는 상태 인디케이터, 그룹을 지원합니다.",
@@ -2958,6 +3103,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
2958
3103
  testDescribe: "Avatar"
2959
3104
  },
2960
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
+ }],
2961
3116
  name: "Progress",
2962
3117
  category: "core",
2963
3118
  description: "진행률 표시. Linear/Circular, determinate/indeterminate 변형과 안전하게 보정된 ARIA 값을 지원합니다.",
@@ -3410,6 +3565,16 @@ const store = useFormStore({ defaultValues: { name: "" } });
3410
3565
  testDescribe: "Card"
3411
3566
  },
3412
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
+ }],
3413
3578
  name: "Checkbox",
3414
3579
  category: "core",
3415
3580
  description: "독립적인 체크박스 컴포넌트. 레이블, 설명, 그룹과 DOM property까지 동기화되는 indeterminate 상태를 지원합니다.",
@@ -3919,6 +4084,11 @@ const store = useFormStore({ defaultValues: { name: "" } });
3919
4084
  type: "number",
3920
4085
  description: "매치 카운트 표시 (toLocaleString 포맷)"
3921
4086
  },
4087
+ {
4088
+ name: "labels",
4089
+ type: "ConditionBuilderLabels",
4090
+ description: "미리보기 문구 오버라이드 (previewSuffix, formatPreview). 기본은 영어 \"matches\""
4091
+ },
3922
4092
  {
3923
4093
  name: "addNestedGroupLabel",
3924
4094
  type: "string",
@@ -3929,6 +4099,17 @@ const store = useFormStore({ defaultValues: { name: "" } });
3929
4099
  testDescribe: "ConditionBuilder"
3930
4100
  },
3931
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
+ }],
3932
4113
  name: "Popover",
3933
4114
  category: "core",
3934
4115
  description: "Popover 프리미티브 래퍼. 트리거 요소에 연결된 플로팅 콘텐츠를 표시합니다.",
@@ -3947,11 +4128,6 @@ const store = useFormStore({ defaultValues: { name: "" } });
3947
4128
  action: "트리거 활성화"
3948
4129
  }],
3949
4130
  props: { PopoverContent: [{
3950
- name: "gutter",
3951
- type: "number",
3952
- default: "8",
3953
- description: "트리거와의 간격 (px)"
3954
- }, {
3955
4131
  name: "className",
3956
4132
  type: "string",
3957
4133
  description: "커스텀 CSS 클래스"
@@ -3988,6 +4164,10 @@ const store = useFormStore({ defaultValues: { name: "" } });
3988
4164
  props: {
3989
4165
  DropdownContent: [{
3990
4166
  name: "gutter",
4167
+ deprecated: {
4168
+ since: "1.13.0",
4169
+ replacement: "sideOffset"
4170
+ },
3991
4171
  type: "number",
3992
4172
  default: "4",
3993
4173
  description: "트리거와의 간격 (px)"
@@ -4736,6 +4916,16 @@ const MyDashboard = createBlock<MyDashboardProps>(
4736
4916
  testDescribe: "VirtualList"
4737
4917
  },
4738
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
+ }],
4739
4929
  name: "LoadingOverlay",
4740
4930
  category: "core",
4741
4931
  description: "로딩 오버레이. 중복 status 없이 하나의 aria-busy 라이브 영역으로 콘텐츠 위 로딩 상태를 표시합니다.",
@@ -4998,6 +5188,7 @@ const MyDashboard = createBlock<MyDashboardProps>(
4998
5188
  description: "페이지에 머무는 인페이지 고지. Alert와 달리 dismiss가 없고, 조건이 참인 동안 레이아웃의 일부로 남습니다.",
4999
5189
  slug: "callout",
5000
5190
  exports: ["Callout"],
5191
+ ariaRole: "status",
5001
5192
  props: { Callout: [
5002
5193
  {
5003
5194
  name: "tone",
@@ -5780,6 +5971,10 @@ const fields = [
5780
5971
  testDescribe: "DomainTable"
5781
5972
  },
5782
5973
  {
5974
+ usage: {
5975
+ bestPractices: ["keyExtractor에는 정렬·필터 후에도 유지되는 행 ID를 사용합니다. 보이는 title이 없으면 ariaLabel로 grid 이름을 지정합니다.", "컬럼 accessor는 행 타입에 맞춰 선언하고, 서버 조회·편집·대규모 가상화가 필요하면 opt-datagrid의 계약을 검토합니다."],
5976
+ avoid: ["배열 인덱스를 행 식별자로 사용하거나 비어 있는 헤더로 액션 컬럼을 익명화하지 않습니다. headerHidden을 사용합니다."]
5977
+ },
5783
5978
  name: "DataTable",
5784
5979
  category: "shell",
5785
5980
  description: "제네릭 데이터 테이블. 제목은 opt-in이고 ariaLabel로 grid를 이름 붙이며, 동적 컬럼·안전 정렬·키보드 리사이즈·Composite 탐색을 지원합니다.",
@@ -5926,6 +6121,17 @@ const data = [
5926
6121
  }]
5927
6122
  },
5928
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
+ }],
5929
6135
  name: "NotificationToast",
5930
6136
  category: "shell",
5931
6137
  description: "토스트 알림 시스템. 포인터/키보드 상호작용 중 자동 닫힘을 일시 정지하고, 중요도별 live region으로 전역 알림을 관리합니다.",
@@ -6120,6 +6326,10 @@ const data = [
6120
6326
  testDescribe: "ActivityFeed"
6121
6327
  },
6122
6328
  {
6329
+ usage: {
6330
+ bestPractices: ["filters의 id를 안정적으로 유지하고 onFilterChange에서 해당 필터의 value를 갱신합니다. onClearAll에서는 적용된 값과 조회 조건을 함께 초기화합니다."],
6331
+ avoid: ["필터 UI 변경만으로 서버 데이터까지 자동 조회된다고 가정하지 않습니다. 비동기 조회와 오류 처리는 호출자가 연결합니다."]
6332
+ },
6123
6333
  name: "FilterBar",
6124
6334
  category: "shell",
6125
6335
  description: "필터링 바 컴포넌트. 검색, 선택, 다중 선택 필터 지원.",
@@ -6939,15 +7149,26 @@ const filters = [
6939
7149
  {
6940
7150
  name: "QueryResultsTable",
6941
7151
  category: "shell",
6942
- description: "쿼리 결과 테이블. SQL 결과와 스크린 리더에 안내되는 로딩, 에러, 빈 상태를 지원합니다.",
7152
+ functionalCategory: "data-display",
7153
+ maturity: "stable",
7154
+ keywords: [
7155
+ "sql",
7156
+ "query",
7157
+ "results",
7158
+ "grid",
7159
+ "table",
7160
+ "virtualized"
7161
+ ],
7162
+ description: "쿼리 결과 테이블. 타입이 표시되는 헤더, 로컬 정렬, 컬럼 리사이즈, 행 상세 패널을 갖추고 행을 윈도잉해 만 행도 스크롤로 다룹니다. NULL·불리언·객체를 빈칸 대신 글자로 씁니다.",
6943
7163
  slug: "query-results-table",
6944
7164
  exports: ["QueryResultsTable"],
7165
+ domlessExports: ["formatQueryCell"],
6945
7166
  props: { QueryResultsTable: [
6946
7167
  {
6947
7168
  name: "columns",
6948
- type: "string[]",
7169
+ type: "Array<string | QueryResultColumn>",
6949
7170
  required: true,
6950
- description: "컬럼 이름"
7171
+ description: "컬럼 이름, 또는 타입을 포함한 컬럼 정의"
6951
7172
  },
6952
7173
  {
6953
7174
  name: "rows",
@@ -6955,6 +7176,12 @@ const filters = [
6955
7176
  required: true,
6956
7177
  description: "결과 행"
6957
7178
  },
7179
+ {
7180
+ name: "maxHeight",
7181
+ type: "number | \"fill\"",
7182
+ default: "400",
7183
+ description: "스크롤 높이(px). fill이면 부모 높이를 채움"
7184
+ },
6958
7185
  {
6959
7186
  name: "loading",
6960
7187
  type: "boolean",
@@ -6965,12 +7192,59 @@ const filters = [
6965
7192
  type: "string",
6966
7193
  description: "assertive 오류 상태"
6967
7194
  },
7195
+ {
7196
+ name: "sortable",
7197
+ type: "boolean",
7198
+ default: "true",
7199
+ description: "헤더 클릭 정렬"
7200
+ },
7201
+ {
7202
+ name: "sort",
7203
+ type: "QueryResultSort | null",
7204
+ description: "제어형 정렬 상태"
7205
+ },
7206
+ {
7207
+ name: "onSortChange",
7208
+ type: "(sort: QueryResultSort | null) => void",
7209
+ description: "정렬 변경"
7210
+ },
7211
+ {
7212
+ name: "rowHeight",
7213
+ type: "number",
7214
+ default: "32",
7215
+ description: "행 높이(px). 윈도잉의 기준"
7216
+ },
7217
+ {
7218
+ name: "rowDetails",
7219
+ type: "boolean",
7220
+ default: "true",
7221
+ description: "행을 상세 패널로 열기"
7222
+ },
7223
+ {
7224
+ name: "footer",
7225
+ type: "ReactNode",
7226
+ description: "행 수 뒤에 붙는 푸터 내용 — 경과 시간, 읽은 바이트"
7227
+ },
6968
7228
  {
6969
7229
  name: "labels",
6970
7230
  type: "QueryResultsTableLabels",
6971
- description: "로딩/빈 상태/행 문구"
7231
+ description: "로딩/빈 상태/행 수/정렬/상세 문구"
6972
7232
  }
6973
7233
  ] },
7234
+ dependencies: [
7235
+ {
7236
+ slug: "field-token",
7237
+ category: "core"
7238
+ },
7239
+ {
7240
+ slug: "flyout",
7241
+ category: "shell"
7242
+ },
7243
+ {
7244
+ slug: "description-list",
7245
+ category: "core"
7246
+ }
7247
+ ],
6974
7248
  testDescribe: "QueryResultsTable"
6975
7249
  },
6976
7250
  {
@@ -7010,12 +7284,12 @@ const filters = [
7010
7284
  },
7011
7285
  {
7012
7286
  name: "onRun",
7013
- type: "(sql: string) => void",
7014
- description: "Mod+Enter 실행 핸들러"
7287
+ type: "(sql: string, context: SqlEditorRunContext) => void",
7288
+ description: "Mod+Enter 실행 핸들러. 두 번째 인자로 선택 영역과 커서 위치"
7015
7289
  },
7016
7290
  {
7017
7291
  name: "onRunAndAdvance",
7018
- type: "(sql: string) => void",
7292
+ type: "(sql: string, context: SqlEditorRunContext) => void",
7019
7293
  description: "Shift+Enter — 실행 후 다음 셀/탭으로 이동"
7020
7294
  },
7021
7295
  {
@@ -7068,6 +7342,18 @@ const filters = [
7068
7342
  type: "boolean",
7069
7343
  description: "실행 힌트/버튼 표시. 기본값은 onRun이 있을 때 표시"
7070
7344
  },
7345
+ {
7346
+ name: "lineNumbers",
7347
+ type: "boolean",
7348
+ default: "true",
7349
+ description: "줄 번호 거터"
7350
+ },
7351
+ {
7352
+ name: "theme",
7353
+ type: "SqlEditorTheme",
7354
+ default: "\"auto\"",
7355
+ description: "편집기 색상. auto는 OptThemeProvider의 resolved mode를 따름"
7356
+ },
7071
7357
  {
7072
7358
  name: "labels",
7073
7359
  type: "SqlEditorLabels",
@@ -7258,6 +7544,221 @@ const filters = [
7258
7544
  }],
7259
7545
  testDescribe: "SegmentBuilder"
7260
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
+ },
7261
7762
  {
7262
7763
  name: "InsightsPanel",
7263
7764
  category: "shell",
@@ -7314,6 +7815,21 @@ const filters = [
7314
7815
  testDescribe: "InsightsPanel"
7315
7816
  },
7316
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
+ }],
7317
7833
  name: "TimeRangeSelector",
7318
7834
  category: "shell",
7319
7835
  description: "시간 범위 선택기. Popover + DateRangePicker + 프리셋 버튼 조합. 12개 시계열 Surface에서 사용.",
@@ -7403,6 +7919,19 @@ const filters = [
7403
7919
  testDescribe: "ExportButton"
7404
7920
  },
7405
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
+ }],
7406
7935
  name: "SummaryRow",
7407
7936
  category: "shell",
7408
7937
  description: "통계 요약 행. StatCard 그리드 레이아웃을 표준화합니다. loading 시 SkeletonCard 표시.",
@@ -9880,6 +10409,777 @@ const messages = [
9880
10409
  avoid: ["선택 초기화를 호출자에게 맡기지 않습니다 — 보이지 않는 행에 일괄 작업이 적용됩니다."]
9881
10410
  },
9882
10411
  testDescribe: "CatalogFrame"
10412
+ },
10413
+ {
10414
+ name: "SchemaTree",
10415
+ category: "shell",
10416
+ functionalCategory: "navigation",
10417
+ maturity: "stable",
10418
+ keywords: [
10419
+ "sql",
10420
+ "schema",
10421
+ "tables",
10422
+ "columns",
10423
+ "tree",
10424
+ "autocomplete"
10425
+ ],
10426
+ description: "쿼리할 수 있는 테이블과 컬럼을 타입 글리프와 함께 보여주는 트리. 테이블·컬럼을 함께 검색하고, 이름을 커서에 삽입하거나 테이블을 미리보기합니다.",
10427
+ slug: "schema-tree",
10428
+ exports: ["SchemaTree"],
10429
+ props: { SchemaTree: [
10430
+ {
10431
+ name: "tables",
10432
+ type: "SchemaTreeTable[]",
10433
+ required: true,
10434
+ description: "테이블과 컬럼"
10435
+ },
10436
+ {
10437
+ name: "loading",
10438
+ type: "boolean",
10439
+ description: "스키마 조회 중"
10440
+ },
10441
+ {
10442
+ name: "defaultExpanded",
10443
+ type: "string[]",
10444
+ description: "처음에 펼칠 테이블. 생략 시 첫 테이블"
10445
+ },
10446
+ {
10447
+ name: "onInsert",
10448
+ type: "(name: string, kind: \"table\" | \"column\", table: string) => void",
10449
+ description: "이름을 편집기 커서에 삽입"
10450
+ },
10451
+ {
10452
+ name: "onPreview",
10453
+ type: "(table: string) => void",
10454
+ description: "테이블 미리보기 실행. SQL은 호출자가 작성"
10455
+ },
10456
+ {
10457
+ name: "header",
10458
+ type: "ReactNode",
10459
+ description: "검색창 위 내용"
10460
+ },
10461
+ {
10462
+ name: "labels",
10463
+ type: "SchemaTreeLabels",
10464
+ description: "문구 오버라이드"
10465
+ }
10466
+ ] },
10467
+ dependencies: [{
10468
+ slug: "field-token",
10469
+ category: "core"
10470
+ }, {
10471
+ slug: "highlight",
10472
+ category: "core"
10473
+ }],
10474
+ ariaRole: "complementary",
10475
+ testDescribe: "SchemaTree"
10476
+ },
10477
+ {
10478
+ name: "QueryToolbar",
10479
+ category: "shell",
10480
+ functionalCategory: "action",
10481
+ maturity: "stable",
10482
+ keywords: [
10483
+ "sql",
10484
+ "run",
10485
+ "cancel",
10486
+ "toolbar",
10487
+ "shortcut"
10488
+ ],
10489
+ description: "실행 버튼 하나가 상태를 말하는 쿼리 툴바. 변경됨/현재/실행 중을 색과 문구로 구분하고, 실행 중이면 취소로 바뀌며, 선택 영역·전체 실행은 분할 버튼 뒤에 둡니다.",
10490
+ slug: "query-toolbar",
10491
+ exports: ["QueryToolbar"],
10492
+ props: { QueryToolbar: [
10493
+ {
10494
+ name: "onRun",
10495
+ type: "() => void",
10496
+ required: true,
10497
+ description: "커서 위치의 문장 실행"
10498
+ },
10499
+ {
10500
+ name: "onCancel",
10501
+ type: "() => void",
10502
+ description: "실행 중 취소. 있으면 버튼이 Cancel로 전환"
10503
+ },
10504
+ {
10505
+ name: "running",
10506
+ type: "boolean",
10507
+ description: "실행 중"
10508
+ },
10509
+ {
10510
+ name: "runState",
10511
+ type: "QueryRunState",
10512
+ default: "\"idle\"",
10513
+ description: "마지막 실행 대비 변경 여부"
10514
+ },
10515
+ {
10516
+ name: "runDisabledReason",
10517
+ type: "string",
10518
+ description: "실행 불가 사유. 툴팁으로 표시"
10519
+ },
10520
+ {
10521
+ name: "onRunSelection",
10522
+ type: "() => void",
10523
+ description: "선택 영역만 실행"
10524
+ },
10525
+ {
10526
+ name: "hasSelection",
10527
+ type: "boolean",
10528
+ description: "선택 영역이 있는지. 없으면 항목 비활성"
10529
+ },
10530
+ {
10531
+ name: "onRunAll",
10532
+ type: "() => void",
10533
+ description: "전체 문장 실행"
10534
+ },
10535
+ {
10536
+ name: "multiStatement",
10537
+ type: "boolean",
10538
+ description: "문장이 둘 이상인지. 아니면 전체 실행을 숨김"
10539
+ },
10540
+ {
10541
+ name: "onSave",
10542
+ type: "() => void",
10543
+ description: "저장 다이얼로그 열기"
10544
+ },
10545
+ {
10546
+ name: "saveDisabled",
10547
+ type: "boolean",
10548
+ description: "저장 비활성"
10549
+ },
10550
+ {
10551
+ name: "children",
10552
+ type: "ReactNode",
10553
+ description: "실행 그룹 뒤의 컨트롤 — 필터, 변수"
10554
+ },
10555
+ {
10556
+ name: "trailing",
10557
+ type: "ReactNode",
10558
+ description: "끝쪽 컨트롤"
10559
+ },
10560
+ {
10561
+ name: "modifierKey",
10562
+ type: "string",
10563
+ description: "단축키 힌트의 수정자 글리프. 기본은 플랫폼 감지"
10564
+ },
10565
+ {
10566
+ name: "labels",
10567
+ type: "QueryToolbarLabels",
10568
+ description: "문구 오버라이드"
10569
+ }
10570
+ ] },
10571
+ dependencies: [
10572
+ {
10573
+ slug: "button",
10574
+ category: "core"
10575
+ },
10576
+ {
10577
+ slug: "kbd",
10578
+ category: "core"
10579
+ },
10580
+ {
10581
+ slug: "dropdown-menu",
10582
+ category: "core"
10583
+ }
10584
+ ],
10585
+ ariaRole: "toolbar",
10586
+ testDescribe: "QueryToolbar"
10587
+ },
10588
+ {
10589
+ name: "QueryOutputPane",
10590
+ category: "shell",
10591
+ functionalCategory: "data-display",
10592
+ maturity: "stable",
10593
+ keywords: [
10594
+ "sql",
10595
+ "results",
10596
+ "chart",
10597
+ "split",
10598
+ "status bar"
10599
+ ],
10600
+ description: "쿼리 결과가 놓이는 출력 패널. 대기·실행 중·오류·취소·완료 상태를 그리고, 완료는 표·차트·좌우 분할로 전환하며, 상태바에 행 수·경과·읽은 바이트·절단 여부를 남깁니다.",
10601
+ slug: "query-output-pane",
10602
+ exports: ["QueryOutputPane"],
10603
+ props: { QueryOutputPane: [
10604
+ {
10605
+ name: "view",
10606
+ type: "QueryOutputView",
10607
+ required: true,
10608
+ description: "표 / 차트 / 분할"
10609
+ },
10610
+ {
10611
+ name: "onViewChange",
10612
+ type: "(view: QueryOutputView) => void",
10613
+ required: true,
10614
+ description: "뷰 전환"
10615
+ },
10616
+ {
10617
+ name: "results",
10618
+ type: "ReactNode",
10619
+ required: true,
10620
+ description: "결과 테이블"
10621
+ },
10622
+ {
10623
+ name: "chart",
10624
+ type: "ReactNode",
10625
+ description: "차트 빌더. 없으면 차트 뷰를 숨김"
10626
+ },
10627
+ {
10628
+ name: "status",
10629
+ type: "\"idle\" | \"running\" | \"success\" | \"error\" | \"cancelled\"",
10630
+ required: true,
10631
+ description: "실행 수명주기"
10632
+ },
10633
+ {
10634
+ name: "error",
10635
+ type: "string",
10636
+ description: "백엔드 오류 원문"
10637
+ },
10638
+ {
10639
+ name: "runningSeconds",
10640
+ type: "number",
10641
+ description: "실행 경과 초. 로딩 상태에 표시"
10642
+ },
10643
+ {
10644
+ name: "onCancel",
10645
+ type: "() => void",
10646
+ description: "실행 취소"
10647
+ },
10648
+ {
10649
+ name: "stats",
10650
+ type: "QueryRunStats",
10651
+ description: "행 수, 경과, 읽은 바이트, 절단 여부"
10652
+ },
10653
+ {
10654
+ name: "actions",
10655
+ type: "ReactNode",
10656
+ description: "뷰 전환 오른쪽 컨트롤"
10657
+ },
10658
+ {
10659
+ name: "statusExtra",
10660
+ type: "ReactNode",
10661
+ description: "상태바 추가 내용"
10662
+ },
10663
+ {
10664
+ name: "banner",
10665
+ type: "ReactNode",
10666
+ description: "출력 위 알림"
10667
+ },
10668
+ {
10669
+ name: "onRetry",
10670
+ type: "() => void",
10671
+ description: "오류 상태 닫기"
10672
+ },
10673
+ {
10674
+ name: "labels",
10675
+ type: "QueryOutputPaneLabels",
10676
+ description: "문구 오버라이드"
10677
+ }
10678
+ ] },
10679
+ dependencies: [
10680
+ {
10681
+ slug: "segmented-control",
10682
+ category: "core"
10683
+ },
10684
+ {
10685
+ slug: "empty-state",
10686
+ category: "core"
10687
+ },
10688
+ {
10689
+ slug: "resizable",
10690
+ category: "core"
10691
+ }
10692
+ ],
10693
+ ariaRole: "region",
10694
+ testDescribe: "QueryOutputPane"
10695
+ },
10696
+ {
10697
+ name: "QueryResultChart",
10698
+ category: "shell",
10699
+ functionalCategory: "data-display",
10700
+ maturity: "stable",
10701
+ keywords: [
10702
+ "sql",
10703
+ "chart",
10704
+ "visualization",
10705
+ "line",
10706
+ "bar",
10707
+ "pie",
10708
+ "number"
10709
+ ],
10710
+ description: "결과 행을 차트로 그리는 빌더. 첫 문자·날짜 컬럼을 축으로, 숫자 컬럼을 값으로 고르고, 한 행 한 숫자면 큰 숫자로 보여줍니다. 설정은 호출자가 들고 있어 저장 쿼리와 함께 보관됩니다.",
10711
+ slug: "query-result-chart",
10712
+ exports: ["QueryResultChart"],
10713
+ domlessExports: ["defaultQueryChartConfig", "numericResultColumns"],
10714
+ props: { QueryResultChart: [
10715
+ {
10716
+ name: "columns",
10717
+ type: "QueryResultColumn[]",
10718
+ required: true,
10719
+ description: "결과 컬럼"
10720
+ },
10721
+ {
10722
+ name: "rows",
10723
+ type: "Array<Record<string, unknown>>",
10724
+ required: true,
10725
+ description: "결과 행"
10726
+ },
10727
+ {
10728
+ name: "config",
10729
+ type: "QueryChartConfig",
10730
+ required: true,
10731
+ description: "차트 종류, 축, 값 컬럼"
10732
+ },
10733
+ {
10734
+ name: "onConfigChange",
10735
+ type: "(config: QueryChartConfig) => void",
10736
+ required: true,
10737
+ description: "설정 변경"
10738
+ },
10739
+ {
10740
+ name: "showControls",
10741
+ type: "boolean",
10742
+ default: "true",
10743
+ description: "컨트롤 표시. 임베드 카드에서는 끔"
10744
+ },
10745
+ {
10746
+ name: "height",
10747
+ type: "number",
10748
+ description: "차트 높이(px). 기본은 부모 채움"
10749
+ },
10750
+ {
10751
+ name: "maxPoints",
10752
+ type: "number",
10753
+ default: "1000",
10754
+ description: "차트에 그릴 최대 행 수"
10755
+ },
10756
+ {
10757
+ name: "labels",
10758
+ type: "QueryResultChartLabels",
10759
+ description: "문구 오버라이드"
10760
+ }
10761
+ ] },
10762
+ dependencies: [
10763
+ {
10764
+ slug: "line-chart",
10765
+ category: "visuals"
10766
+ },
10767
+ {
10768
+ slug: "bar-chart",
10769
+ category: "visuals"
10770
+ },
10771
+ {
10772
+ slug: "area-chart",
10773
+ category: "visuals"
10774
+ },
10775
+ {
10776
+ slug: "pie-chart",
10777
+ category: "visuals"
10778
+ },
10779
+ {
10780
+ slug: "segmented-control",
10781
+ category: "core"
10782
+ },
10783
+ {
10784
+ slug: "select",
10785
+ category: "core"
10786
+ }
10787
+ ],
10788
+ testDescribe: "QueryResultChart"
10789
+ },
10790
+ {
10791
+ name: "QueryFiltersMenu",
10792
+ category: "shell",
10793
+ functionalCategory: "input",
10794
+ maturity: "stable",
10795
+ keywords: [
10796
+ "sql",
10797
+ "filters",
10798
+ "date range",
10799
+ "placeholder",
10800
+ "where"
10801
+ ],
10802
+ description: "기간과 속성 조건을 데이터로 들고 {filters} 플레이스홀더로 SQL에 전달하는 메뉴. 플레이스홀더가 없으면 조건이 적용되지 않는다고 경고하고 삽입 버튼을 제공합니다.",
10803
+ slug: "query-filters-menu",
10804
+ exports: ["QueryFiltersMenu"],
10805
+ domlessExports: [
10806
+ "EMPTY_QUERY_FILTERS",
10807
+ "VALUELESS_OPERATORS",
10808
+ "hasActiveQueryFilters",
10809
+ "countActiveQueryFilters"
10810
+ ],
10811
+ props: { QueryFiltersMenu: [
10812
+ {
10813
+ name: "value",
10814
+ type: "QueryFilters",
10815
+ required: true,
10816
+ description: "기간과 속성 필터"
10817
+ },
10818
+ {
10819
+ name: "onChange",
10820
+ type: "(filters: QueryFilters) => void",
10821
+ required: true,
10822
+ description: "필터 변경"
10823
+ },
10824
+ {
10825
+ name: "fields",
10826
+ type: "QueryFilterField[]",
10827
+ required: true,
10828
+ description: "필드 선택지"
10829
+ },
10830
+ {
10831
+ name: "placeholderPresent",
10832
+ type: "boolean",
10833
+ description: "SQL에 {filters}가 있는지"
10834
+ },
10835
+ {
10836
+ name: "onInsertPlaceholder",
10837
+ type: "() => void",
10838
+ description: "{filters}를 커서에 삽입"
10839
+ },
10840
+ {
10841
+ name: "onLoadValues",
10842
+ type: "(field: string) => Promise<string[]>",
10843
+ description: "필드의 알려진 값. 값 입력란의 제안으로 쓰이며 자유 입력도 허용"
10844
+ },
10845
+ {
10846
+ name: "presets",
10847
+ type: "DateRangePreset[]",
10848
+ description: "기간 프리셋"
10849
+ },
10850
+ {
10851
+ name: "disabled",
10852
+ type: "boolean",
10853
+ description: "비활성"
10854
+ },
10855
+ {
10856
+ name: "labels",
10857
+ type: "QueryFiltersMenuLabels",
10858
+ description: "문구 오버라이드"
10859
+ }
10860
+ ] },
10861
+ dependencies: [
10862
+ {
10863
+ slug: "popover",
10864
+ category: "core"
10865
+ },
10866
+ {
10867
+ slug: "date-range-picker",
10868
+ category: "core"
10869
+ },
10870
+ {
10871
+ slug: "select",
10872
+ category: "core"
10873
+ },
10874
+ {
10875
+ slug: "combobox",
10876
+ category: "core"
10877
+ },
10878
+ {
10879
+ slug: "callout",
10880
+ category: "core"
10881
+ }
10882
+ ],
10883
+ testDescribe: "QueryFiltersMenu"
10884
+ },
10885
+ {
10886
+ name: "QueryVariablesMenu",
10887
+ category: "shell",
10888
+ functionalCategory: "input",
10889
+ maturity: "stable",
10890
+ keywords: [
10891
+ "sql",
10892
+ "variables",
10893
+ "parameters",
10894
+ "placeholder"
10895
+ ],
10896
+ description: "실행마다 바뀌는 값을 이름 붙여 두는 변수 메뉴. SQL은 {variables.name}으로 참조하고 치환은 호출자가 합니다.",
10897
+ slug: "query-variables-menu",
10898
+ exports: ["QueryVariablesMenu"],
10899
+ domlessExports: ["isValidQueryVariableName"],
10900
+ props: { QueryVariablesMenu: [
10901
+ {
10902
+ name: "value",
10903
+ type: "QueryVariable[]",
10904
+ required: true,
10905
+ description: "변수 목록"
10906
+ },
10907
+ {
10908
+ name: "onChange",
10909
+ type: "(variables: QueryVariable[]) => void",
10910
+ required: true,
10911
+ description: "변수 변경"
10912
+ },
10913
+ {
10914
+ name: "onInsert",
10915
+ type: "(name: string) => void",
10916
+ description: "{variables.name}을 커서에 삽입"
10917
+ },
10918
+ {
10919
+ name: "disabled",
10920
+ type: "boolean",
10921
+ description: "비활성"
10922
+ },
10923
+ {
10924
+ name: "disabledReason",
10925
+ type: "string",
10926
+ description: "비활성 사유. 툴팁으로 표시"
10927
+ },
10928
+ {
10929
+ name: "labels",
10930
+ type: "QueryVariablesMenuLabels",
10931
+ description: "문구 오버라이드"
10932
+ }
10933
+ ] },
10934
+ dependencies: [{
10935
+ slug: "popover",
10936
+ category: "core"
10937
+ }, {
10938
+ slug: "input",
10939
+ category: "core"
10940
+ }],
10941
+ testDescribe: "QueryVariablesMenu"
10942
+ },
10943
+ {
10944
+ name: "QueryHistoryList",
10945
+ category: "shell",
10946
+ functionalCategory: "data-display",
10947
+ maturity: "stable",
10948
+ keywords: [
10949
+ "sql",
10950
+ "history",
10951
+ "recent",
10952
+ "rerun"
10953
+ ],
10954
+ description: "실행 이력 목록. 각 실행의 성공/실패, 시각, 행 수, 경과를 보여주고 편집기로 되돌리거나 다시 실행합니다.",
10955
+ slug: "query-history-list",
10956
+ exports: ["QueryHistoryList"],
10957
+ props: { QueryHistoryList: [
10958
+ {
10959
+ name: "items",
10960
+ type: "QueryHistoryItem[]",
10961
+ required: true,
10962
+ description: "최근 실행부터"
10963
+ },
10964
+ {
10965
+ name: "loading",
10966
+ type: "boolean",
10967
+ description: "이력 조회 중"
10968
+ },
10969
+ {
10970
+ name: "onLoad",
10971
+ type: "(item: QueryHistoryItem) => void",
10972
+ description: "쿼리를 편집기로"
10973
+ },
10974
+ {
10975
+ name: "onRerun",
10976
+ type: "(item: QueryHistoryItem) => void",
10977
+ description: "그대로 다시 실행"
10978
+ },
10979
+ {
10980
+ name: "onClear",
10981
+ type: "() => void",
10982
+ description: "이력 비우기"
10983
+ },
10984
+ {
10985
+ name: "header",
10986
+ type: "ReactNode",
10987
+ description: "목록 위 내용"
10988
+ },
10989
+ {
10990
+ name: "labels",
10991
+ type: "QueryHistoryListLabels",
10992
+ description: "문구 오버라이드"
10993
+ }
10994
+ ] },
10995
+ dependencies: [{
10996
+ slug: "button",
10997
+ category: "core"
10998
+ }],
10999
+ ariaRole: "region",
11000
+ testDescribe: "QueryHistoryList"
11001
+ },
11002
+ {
11003
+ name: "SaveQueryDialog",
11004
+ category: "shell",
11005
+ functionalCategory: "overlay",
11006
+ maturity: "stable",
11007
+ keywords: [
11008
+ "sql",
11009
+ "save",
11010
+ "dialog",
11011
+ "saved query"
11012
+ ],
11013
+ description: "쿼리에 이름·설명·공개 여부를 붙여 저장하는 다이얼로그. 편집기에 문장이 여럿이면 어느 문장을 저장할지 넘겨 보며 고릅니다.",
11014
+ slug: "save-query-dialog",
11015
+ exports: ["SaveQueryDialog"],
11016
+ props: { SaveQueryDialog: [
11017
+ {
11018
+ name: "open",
11019
+ type: "boolean",
11020
+ required: true,
11021
+ description: "열림"
11022
+ },
11023
+ {
11024
+ name: "onOpenChange",
11025
+ type: "(open: boolean) => void",
11026
+ required: true,
11027
+ description: "열림 상태 변경"
11028
+ },
11029
+ {
11030
+ name: "onSubmit",
11031
+ type: "(values: SaveQueryValues) => void | Promise<void>",
11032
+ required: true,
11033
+ description: "저장"
11034
+ },
11035
+ {
11036
+ name: "candidates",
11037
+ type: "string[]",
11038
+ required: true,
11039
+ description: "저장할 SQL 후보. 문장이 여럿이면 여러 개"
11040
+ },
11041
+ {
11042
+ name: "initialCandidate",
11043
+ type: "number",
11044
+ default: "0",
11045
+ description: "처음 보여줄 후보"
11046
+ },
11047
+ {
11048
+ name: "candidateIsSelection",
11049
+ type: "boolean",
11050
+ description: "후보가 선택 영역인지"
11051
+ },
11052
+ {
11053
+ name: "initialValues",
11054
+ type: "Partial<Omit<SaveQueryValues, \"sql\">>",
11055
+ description: "기존 저장 쿼리를 수정할 때의 초기값"
11056
+ },
11057
+ {
11058
+ name: "mode",
11059
+ type: "\"create\" | \"update\"",
11060
+ default: "\"create\"",
11061
+ description: "새로 저장 / 수정"
11062
+ },
11063
+ {
11064
+ name: "saving",
11065
+ type: "boolean",
11066
+ description: "저장 중"
11067
+ },
11068
+ {
11069
+ name: "error",
11070
+ type: "string",
11071
+ description: "저장 실패 메시지"
11072
+ },
11073
+ {
11074
+ name: "labels",
11075
+ type: "SaveQueryDialogLabels",
11076
+ description: "문구 오버라이드"
11077
+ }
11078
+ ] },
11079
+ dependencies: [
11080
+ {
11081
+ slug: "dialog",
11082
+ category: "core"
11083
+ },
11084
+ {
11085
+ slug: "input",
11086
+ category: "core"
11087
+ },
11088
+ {
11089
+ slug: "textarea",
11090
+ category: "core"
11091
+ },
11092
+ {
11093
+ slug: "switch",
11094
+ category: "core"
11095
+ }
11096
+ ],
11097
+ testDescribe: "SaveQueryDialog"
11098
+ },
11099
+ {
11100
+ name: "QueryWorkspace",
11101
+ category: "shell",
11102
+ functionalCategory: "layout",
11103
+ maturity: "stable",
11104
+ keywords: [
11105
+ "sql",
11106
+ "workspace",
11107
+ "layout",
11108
+ "resizable",
11109
+ "sidebar"
11110
+ ],
11111
+ description: "스키마·편집기·출력의 3-pane 쿼리 워크스페이스. 패널은 리사이즈되고 사이드바는 접히며, storageKey로 레이아웃을 기억합니다. 툴바·편집기·출력은 슬롯입니다.",
11112
+ slug: "query-workspace",
11113
+ exports: ["QueryWorkspace"],
11114
+ props: { QueryWorkspace: [
11115
+ {
11116
+ name: "sidebar",
11117
+ type: "ReactNode",
11118
+ description: "왼쪽 패널 내용"
11119
+ },
11120
+ {
11121
+ name: "sidebarOpen",
11122
+ type: "boolean",
11123
+ description: "제어형 사이드바 열림"
11124
+ },
11125
+ {
11126
+ name: "onSidebarOpenChange",
11127
+ type: "(open: boolean) => void",
11128
+ description: "사이드바 열림 변경"
11129
+ },
11130
+ {
11131
+ name: "header",
11132
+ type: "ReactNode",
11133
+ description: "툴바 위 — 탭이나 페이지 헤더"
11134
+ },
11135
+ {
11136
+ name: "toolbar",
11137
+ type: "ReactNode",
11138
+ description: "툴바"
11139
+ },
11140
+ {
11141
+ name: "editor",
11142
+ type: "ReactNode",
11143
+ required: true,
11144
+ description: "편집기"
11145
+ },
11146
+ {
11147
+ name: "output",
11148
+ type: "ReactNode",
11149
+ required: true,
11150
+ description: "출력"
11151
+ },
11152
+ {
11153
+ name: "storageKey",
11154
+ type: "string",
11155
+ description: "패널 크기를 localStorage에 기억할 키"
11156
+ },
11157
+ {
11158
+ name: "defaultSidebarSize",
11159
+ type: "number",
11160
+ default: "22",
11161
+ description: "사이드바 폭(%)"
11162
+ },
11163
+ {
11164
+ name: "defaultEditorSize",
11165
+ type: "number",
11166
+ default: "40",
11167
+ description: "편집기 높이(%)"
11168
+ },
11169
+ {
11170
+ name: "labels",
11171
+ type: "QueryWorkspaceLabels",
11172
+ description: "문구 오버라이드"
11173
+ }
11174
+ ] },
11175
+ dependencies: [{
11176
+ slug: "resizable",
11177
+ category: "core"
11178
+ }, {
11179
+ slug: "button",
11180
+ category: "core"
11181
+ }],
11182
+ testDescribe: "QueryWorkspace"
9883
11183
  }
9884
11184
  ];
9885
11185
  //#endregion