@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.
Files changed (37) hide show
  1. package/COMPONENT_CATALOG.md +426 -58
  2. package/README.md +9 -7
  3. package/dist/core/index.cjs +1 -1
  4. package/dist/core/index.d.cts +2 -2
  5. package/dist/core/index.d.ts +2 -2
  6. package/dist/core/index.js +1 -1
  7. package/dist/docs/02-components/01-core.md +225 -26
  8. package/dist/docs/02-components/02-visuals.md +1 -1
  9. package/dist/docs/02-components/03-shells.md +203 -32
  10. package/dist/docs/02-components/04-surfaces.md +1 -1
  11. package/dist/docs/02-components/index.md +4 -4
  12. package/dist/{field-sidebar-Dz7J4ett.d.ts → field-sidebar-CJ4HxP8D.d.cts} +1 -1
  13. package/dist/{field-sidebar-D-rwuN_w.d.cts → field-sidebar-DnhpcT2A.d.ts} +1 -1
  14. package/dist/{field-sidebar-CxxSK4JO.cjs → field-sidebar-ePqFX6MN.cjs} +13 -12
  15. package/dist/{field-sidebar-CRdToHRO.js → field-sidebar-mSVCMAZp.js} +13 -12
  16. package/dist/id-registry.cjs +8 -0
  17. package/dist/id-registry.js +8 -0
  18. package/dist/id-registry.json +17 -0
  19. package/dist/index.cjs +756 -51
  20. package/dist/index.d.cts +196 -10
  21. package/dist/index.d.ts +196 -10
  22. package/dist/index.js +750 -53
  23. package/dist/{key-pad-menu-0KWOnELe.cjs → key-pad-menu-BgtSL3xe.cjs} +9 -6
  24. package/dist/{key-pad-menu-DMmbQ1jF.js → key-pad-menu-CqFI6la9.js} +9 -6
  25. package/dist/{key-pad-menu-BOVu97bj.d.cts → key-pad-menu-D6pYzaj-.d.cts} +12 -3
  26. package/dist/{key-pad-menu-NlZ9zNF8.d.ts → key-pad-menu-Yh7Fpb8m.d.ts} +12 -3
  27. package/dist/meta.cjs +430 -19
  28. package/dist/meta.js +430 -19
  29. package/dist/shells/index.cjs +1 -1
  30. package/dist/shells/index.d.cts +1 -1
  31. package/dist/shells/index.d.ts +1 -1
  32. package/dist/shells/index.js +1 -1
  33. package/dist/{text-truncate-eMzvU_7k.d.cts → text-truncate-Bbul-jMD.d.cts} +1 -30
  34. package/dist/{text-truncate-eMzvU_7k.d.ts → text-truncate-Bbul-jMD.d.ts} +1 -30
  35. package/dist/theme/server.d.cts +2 -2
  36. package/dist/theme/server.d.ts +2 -2
  37. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_text_truncate = require("./text-truncate-BeYW6XZu.cjs");
4
- const require_key_pad_menu = require("./key-pad-menu-0KWOnELe.cjs");
5
- const require_field_sidebar = require("./field-sidebar-CxxSK4JO.cjs");
4
+ const require_key_pad_menu = require("./key-pad-menu-BgtSL3xe.cjs");
5
+ const require_field_sidebar = require("./field-sidebar-ePqFX6MN.cjs");
6
6
  let react_jsx_runtime = require("react/jsx-runtime");
7
7
  let react = require("react");
8
8
  react = require_text_truncate.__toESM(react, 1);
@@ -93,7 +93,7 @@ const _excluded = [
93
93
  "disabled",
94
94
  "onClick"
95
95
  ];
96
- const defaultLabels$33 = {
96
+ const defaultLabels$34 = {
97
97
  copy: "복사",
98
98
  copied: "복사됨",
99
99
  failed: "복사할 수 없습니다"
@@ -140,7 +140,7 @@ const stateTone = {
140
140
  */
141
141
  function CopyButton(_ref) {
142
142
  let { value, variant = "icon", size = "md", feedbackDuration = 1500, onCopy, labels: customLabels, className, disabled, onClick } = _ref, props = require_text_truncate._objectWithoutProperties(_ref, _excluded);
143
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$33), customLabels);
143
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$34), customLabels);
144
144
  const [state, setState] = (0, react.useState)("idle");
145
145
  const timer = (0, react.useRef)(null);
146
146
  (0, react.useEffect)(() => () => {
@@ -265,7 +265,7 @@ function PageAside({ header, children, footer, widthClassName = "w-60", label, h
265
265
  PageAside.displayName = "PageAside";
266
266
  //#endregion
267
267
  //#region src/shells/task-list.tsx
268
- const defaultLabels$32 = {
268
+ const defaultLabels$33 = {
269
269
  title: "작업 목록",
270
270
  emptyMessage: "작업이 없습니다",
271
271
  completedOf: "{completed}/{total} 완료",
@@ -285,9 +285,9 @@ const defaultLabels$32 = {
285
285
  };
286
286
  /** Renders the task list component. */
287
287
  function TaskList({ tasks, title, onTaskClick, onTaskStatusChange, emptyMessage, showSubtasks = false, maxItems, labels: customLabels, className }) {
288
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$32), customLabels), {}, {
289
- priority: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$32.priority), customLabels === null || customLabels === void 0 ? void 0 : customLabels.priority),
290
- status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$32.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status)
288
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$33), customLabels), {}, {
289
+ priority: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$33.priority), customLabels === null || customLabels === void 0 ? void 0 : customLabels.priority),
290
+ status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$33.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status)
291
291
  });
292
292
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
293
293
  const displayEmptyMessage = emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : labels.emptyMessage;
@@ -445,7 +445,7 @@ function TaskList({ tasks, title, onTaskClick, onTaskStatusChange, emptyMessage,
445
445
  }
446
446
  //#endregion
447
447
  //#region src/shells/activity-feed.tsx
448
- const defaultLabels$31 = {
448
+ const defaultLabels$32 = {
449
449
  title: "활동 피드",
450
450
  emptyMessage: "활동이 없습니다",
451
451
  moreItems: "{count}개 더 보기",
@@ -495,24 +495,24 @@ const variantBorder = {
495
495
  function ActivityFeed({ activities, title, maxItems, onActivityClick, onShowMore, emptyMessage, labels: customLabels, locale = "ko-KR", loading = false, layout = "list", renderActions, className }) {
496
496
  var _customLabels$timeAgo, _customLabels$timeAgo2, _customLabels$timeAgo3, _customLabels$timeAgo4, _customLabels$timeAgo5, _customLabels$timeAgo6, _customLabels$timeAgo7, _customLabels$timeAgo8, _customLabels$activit, _customLabels$activit2, _customLabels$activit3, _customLabels$activit4, _customLabels$activit5, _customLabels$activit6, _customLabels$activit7, _customLabels$activit8, _customLabels$activit9, _customLabels$activit10, _customLabels$activit11, _customLabels$activit12, _customLabels$activit13, _customLabels$activit14, _customLabels$title, _customLabels$emptyMe, _customLabels$moreIte;
497
497
  const mergedTimeAgo = {
498
- justNow: (_customLabels$timeAgo = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo2 = customLabels.timeAgo) === null || _customLabels$timeAgo2 === void 0 ? void 0 : _customLabels$timeAgo2.justNow) !== null && _customLabels$timeAgo !== void 0 ? _customLabels$timeAgo : defaultLabels$31.timeAgo.justNow,
499
- minutesAgo: (_customLabels$timeAgo3 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo4 = customLabels.timeAgo) === null || _customLabels$timeAgo4 === void 0 ? void 0 : _customLabels$timeAgo4.minutesAgo) !== null && _customLabels$timeAgo3 !== void 0 ? _customLabels$timeAgo3 : defaultLabels$31.timeAgo.minutesAgo,
500
- hoursAgo: (_customLabels$timeAgo5 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo6 = customLabels.timeAgo) === null || _customLabels$timeAgo6 === void 0 ? void 0 : _customLabels$timeAgo6.hoursAgo) !== null && _customLabels$timeAgo5 !== void 0 ? _customLabels$timeAgo5 : defaultLabels$31.timeAgo.hoursAgo,
501
- daysAgo: (_customLabels$timeAgo7 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo8 = customLabels.timeAgo) === null || _customLabels$timeAgo8 === void 0 ? void 0 : _customLabels$timeAgo8.daysAgo) !== null && _customLabels$timeAgo7 !== void 0 ? _customLabels$timeAgo7 : defaultLabels$31.timeAgo.daysAgo
498
+ justNow: (_customLabels$timeAgo = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo2 = customLabels.timeAgo) === null || _customLabels$timeAgo2 === void 0 ? void 0 : _customLabels$timeAgo2.justNow) !== null && _customLabels$timeAgo !== void 0 ? _customLabels$timeAgo : defaultLabels$32.timeAgo.justNow,
499
+ minutesAgo: (_customLabels$timeAgo3 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo4 = customLabels.timeAgo) === null || _customLabels$timeAgo4 === void 0 ? void 0 : _customLabels$timeAgo4.minutesAgo) !== null && _customLabels$timeAgo3 !== void 0 ? _customLabels$timeAgo3 : defaultLabels$32.timeAgo.minutesAgo,
500
+ hoursAgo: (_customLabels$timeAgo5 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo6 = customLabels.timeAgo) === null || _customLabels$timeAgo6 === void 0 ? void 0 : _customLabels$timeAgo6.hoursAgo) !== null && _customLabels$timeAgo5 !== void 0 ? _customLabels$timeAgo5 : defaultLabels$32.timeAgo.hoursAgo,
501
+ daysAgo: (_customLabels$timeAgo7 = customLabels === null || customLabels === void 0 || (_customLabels$timeAgo8 = customLabels.timeAgo) === null || _customLabels$timeAgo8 === void 0 ? void 0 : _customLabels$timeAgo8.daysAgo) !== null && _customLabels$timeAgo7 !== void 0 ? _customLabels$timeAgo7 : defaultLabels$32.timeAgo.daysAgo
502
502
  };
503
503
  const mergedActivityVerbs = {
504
- created: (_customLabels$activit = customLabels === null || customLabels === void 0 || (_customLabels$activit2 = customLabels.activityVerbs) === null || _customLabels$activit2 === void 0 ? void 0 : _customLabels$activit2.created) !== null && _customLabels$activit !== void 0 ? _customLabels$activit : defaultLabels$31.activityVerbs.created,
505
- updated: (_customLabels$activit3 = customLabels === null || customLabels === void 0 || (_customLabels$activit4 = customLabels.activityVerbs) === null || _customLabels$activit4 === void 0 ? void 0 : _customLabels$activit4.updated) !== null && _customLabels$activit3 !== void 0 ? _customLabels$activit3 : defaultLabels$31.activityVerbs.updated,
506
- completed: (_customLabels$activit5 = customLabels === null || customLabels === void 0 || (_customLabels$activit6 = customLabels.activityVerbs) === null || _customLabels$activit6 === void 0 ? void 0 : _customLabels$activit6.completed) !== null && _customLabels$activit5 !== void 0 ? _customLabels$activit5 : defaultLabels$31.activityVerbs.completed,
507
- commented: (_customLabels$activit7 = customLabels === null || customLabels === void 0 || (_customLabels$activit8 = customLabels.activityVerbs) === null || _customLabels$activit8 === void 0 ? void 0 : _customLabels$activit8.commented) !== null && _customLabels$activit7 !== void 0 ? _customLabels$activit7 : defaultLabels$31.activityVerbs.commented,
508
- assigned: (_customLabels$activit9 = customLabels === null || customLabels === void 0 || (_customLabels$activit10 = customLabels.activityVerbs) === null || _customLabels$activit10 === void 0 ? void 0 : _customLabels$activit10.assigned) !== null && _customLabels$activit9 !== void 0 ? _customLabels$activit9 : defaultLabels$31.activityVerbs.assigned,
509
- deployed: (_customLabels$activit11 = customLabels === null || customLabels === void 0 || (_customLabels$activit12 = customLabels.activityVerbs) === null || _customLabels$activit12 === void 0 ? void 0 : _customLabels$activit12.deployed) !== null && _customLabels$activit11 !== void 0 ? _customLabels$activit11 : defaultLabels$31.activityVerbs.deployed,
510
- merged: (_customLabels$activit13 = customLabels === null || customLabels === void 0 || (_customLabels$activit14 = customLabels.activityVerbs) === null || _customLabels$activit14 === void 0 ? void 0 : _customLabels$activit14.merged) !== null && _customLabels$activit13 !== void 0 ? _customLabels$activit13 : defaultLabels$31.activityVerbs.merged
504
+ created: (_customLabels$activit = customLabels === null || customLabels === void 0 || (_customLabels$activit2 = customLabels.activityVerbs) === null || _customLabels$activit2 === void 0 ? void 0 : _customLabels$activit2.created) !== null && _customLabels$activit !== void 0 ? _customLabels$activit : defaultLabels$32.activityVerbs.created,
505
+ updated: (_customLabels$activit3 = customLabels === null || customLabels === void 0 || (_customLabels$activit4 = customLabels.activityVerbs) === null || _customLabels$activit4 === void 0 ? void 0 : _customLabels$activit4.updated) !== null && _customLabels$activit3 !== void 0 ? _customLabels$activit3 : defaultLabels$32.activityVerbs.updated,
506
+ completed: (_customLabels$activit5 = customLabels === null || customLabels === void 0 || (_customLabels$activit6 = customLabels.activityVerbs) === null || _customLabels$activit6 === void 0 ? void 0 : _customLabels$activit6.completed) !== null && _customLabels$activit5 !== void 0 ? _customLabels$activit5 : defaultLabels$32.activityVerbs.completed,
507
+ commented: (_customLabels$activit7 = customLabels === null || customLabels === void 0 || (_customLabels$activit8 = customLabels.activityVerbs) === null || _customLabels$activit8 === void 0 ? void 0 : _customLabels$activit8.commented) !== null && _customLabels$activit7 !== void 0 ? _customLabels$activit7 : defaultLabels$32.activityVerbs.commented,
508
+ assigned: (_customLabels$activit9 = customLabels === null || customLabels === void 0 || (_customLabels$activit10 = customLabels.activityVerbs) === null || _customLabels$activit10 === void 0 ? void 0 : _customLabels$activit10.assigned) !== null && _customLabels$activit9 !== void 0 ? _customLabels$activit9 : defaultLabels$32.activityVerbs.assigned,
509
+ deployed: (_customLabels$activit11 = customLabels === null || customLabels === void 0 || (_customLabels$activit12 = customLabels.activityVerbs) === null || _customLabels$activit12 === void 0 ? void 0 : _customLabels$activit12.deployed) !== null && _customLabels$activit11 !== void 0 ? _customLabels$activit11 : defaultLabels$32.activityVerbs.deployed,
510
+ merged: (_customLabels$activit13 = customLabels === null || customLabels === void 0 || (_customLabels$activit14 = customLabels.activityVerbs) === null || _customLabels$activit14 === void 0 ? void 0 : _customLabels$activit14.merged) !== null && _customLabels$activit13 !== void 0 ? _customLabels$activit13 : defaultLabels$32.activityVerbs.merged
511
511
  };
512
512
  const labels = {
513
- title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$31.title,
514
- emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$31.emptyMessage,
515
- moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$31.moreItems,
513
+ title: (_customLabels$title = customLabels === null || customLabels === void 0 ? void 0 : customLabels.title) !== null && _customLabels$title !== void 0 ? _customLabels$title : defaultLabels$32.title,
514
+ emptyMessage: (_customLabels$emptyMe = customLabels === null || customLabels === void 0 ? void 0 : customLabels.emptyMessage) !== null && _customLabels$emptyMe !== void 0 ? _customLabels$emptyMe : defaultLabels$32.emptyMessage,
515
+ moreItems: (_customLabels$moreIte = customLabels === null || customLabels === void 0 ? void 0 : customLabels.moreItems) !== null && _customLabels$moreIte !== void 0 ? _customLabels$moreIte : defaultLabels$32.moreItems,
516
516
  timeAgo: mergedTimeAgo,
517
517
  activityVerbs: mergedActivityVerbs
518
518
  };
@@ -640,13 +640,13 @@ function ActivityFeed({ activities, title, maxItems, onActivityClick, onShowMore
640
640
  ActivityFeed.displayName = "ActivityFeed";
641
641
  //#endregion
642
642
  //#region src/shells/filter-bar.tsx
643
- const defaultLabels$30 = {
643
+ const defaultLabels$31 = {
644
644
  clearAll: "필터 초기화",
645
645
  searchPlaceholder: "검색..."
646
646
  };
647
647
  /** Renders the filter bar component. */
648
648
  function FilterBar({ filters, onFilterChange, onDateRangeChange, onClearAll, labels: customLabels, presetActions, className }) {
649
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$30), customLabels);
649
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$31), customLabels);
650
650
  const [searchValues, setSearchValues] = (0, react.useState)({});
651
651
  const [tagInputValues, setTagInputValues] = (0, react.useState)({});
652
652
  const [multiSelectOpen, setMultiSelectOpen] = (0, react.useState)({});
@@ -872,10 +872,10 @@ function matchesShortcut(event, shortcut) {
872
872
  if (requiresMeta && !event.metaKey) return false;
873
873
  return event.key.toLowerCase() === key;
874
874
  }
875
- const defaultLabels$29 = { title: "빠른 작업" };
875
+ const defaultLabels$30 = { title: "빠른 작업" };
876
876
  /** Renders the quick actions component. */
877
877
  function QuickActions({ actions, title, layout = "grid", columns = 3, showDescriptionInGrid = false, bindShortcuts = false, labels: customLabels, className }) {
878
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$29), customLabels);
878
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$30), customLabels);
879
879
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
880
880
  const titleId = (0, react.useId)();
881
881
  (0, react.useEffect)(() => {
@@ -953,7 +953,7 @@ function QuickActions({ actions, title, layout = "grid", columns = 3, showDescri
953
953
  QuickActions.displayName = "QuickActions";
954
954
  //#endregion
955
955
  //#region src/shells/team-member-list.tsx
956
- const defaultLabels$28 = {
956
+ const defaultLabels$29 = {
957
957
  title: "팀원",
958
958
  emptyMessage: "팀원이 없습니다",
959
959
  onlineCount: "{online}/{total} 온라인",
@@ -974,7 +974,7 @@ const statusDot = {
974
974
  };
975
975
  /** Renders the team member list component. */
976
976
  function TeamMemberList({ members, title, layout = "list", maxDisplay, onMemberClick, onShowMore, emptyMessage, labels: customLabels, className }) {
977
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$28), customLabels), {}, { status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$28.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status) });
977
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$29), customLabels), {}, { status: require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$29.status), customLabels === null || customLabels === void 0 ? void 0 : customLabels.status) });
978
978
  const displayTitle = title !== null && title !== void 0 ? title : labels.title;
979
979
  const displayEmptyMessage = emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : labels.emptyMessage;
980
980
  const titleId = (0, react.useId)();
@@ -1874,7 +1874,7 @@ const COLOR_PRESETS = [
1874
1874
  "#f97316",
1875
1875
  "#14b8a6"
1876
1876
  ];
1877
- const defaultLabels$27 = {
1877
+ const defaultLabels$28 = {
1878
1878
  eventLabelPrefix: "Event",
1879
1879
  defaultAriaLabel: "Event icon"
1880
1880
  };
@@ -1905,7 +1905,7 @@ function getEventIconStyle(color) {
1905
1905
  /** Renders the event icon component. */
1906
1906
  function EventIcon({ icon, color, size = "md", className, labels: customLabels }) {
1907
1907
  var _iconEntry$label;
1908
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$27), customLabels);
1908
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$28), customLabels);
1909
1909
  const sizes = SIZE_MAP[size];
1910
1910
  const resolvedColor = (color === null || color === void 0 ? void 0 : color.trim()) ? color : void 0;
1911
1911
  const iconEntry = typeof icon === "string" ? require_text_truncate.getIconRegistryEntry(icon) : void 0;
@@ -1997,14 +1997,14 @@ function ConnectionIndicator({ status, tone, label, showLabel = true, pulse, cla
1997
1997
  ConnectionIndicator.displayName = "ConnectionIndicator";
1998
1998
  //#endregion
1999
1999
  //#region src/shells/floating-action-bar.tsx
2000
- const defaultLabels$26 = {
2000
+ const defaultLabels$27 = {
2001
2001
  toolbarAriaLabel: "Actions",
2002
2002
  selectedLabel: (count) => `${count} selected`,
2003
2003
  dismissAriaLabel: "Dismiss"
2004
2004
  };
2005
2005
  /** Renders the floating action bar component. */
2006
2006
  function FloatingActionBar({ actions, selectedCount, onAction, onDismiss, className, labels: customLabels }) {
2007
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$26), customLabels);
2007
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$27), customLabels);
2008
2008
  if (actions.length === 0) return null;
2009
2009
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2010
2010
  role: "toolbar",
@@ -2041,7 +2041,7 @@ function FloatingActionBar({ actions, selectedCount, onAction, onDismiss, classN
2041
2041
  FloatingActionBar.displayName = "FloatingActionBar";
2042
2042
  //#endregion
2043
2043
  //#region src/shells/template-picker.tsx
2044
- const defaultLabels$25 = {
2044
+ const defaultLabels$26 = {
2045
2045
  ariaLabel: "Templates",
2046
2046
  defaultCategory: "General",
2047
2047
  searchPlaceholder: "Search templates...",
@@ -2049,7 +2049,7 @@ const defaultLabels$25 = {
2049
2049
  };
2050
2050
  /** Renders the template picker component. */
2051
2051
  function TemplatePicker({ templates, selected, onSelect, labels: customLabels, showSearch = false, className }) {
2052
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$25), customLabels);
2052
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$26), customLabels);
2053
2053
  const [search, setSearch] = react.useState("");
2054
2054
  const filtered = react.useMemo(() => {
2055
2055
  if (!showSearch || !search) return templates;
@@ -2135,7 +2135,7 @@ function TemplatePicker({ templates, selected, onSelect, labels: customLabels, s
2135
2135
  TemplatePicker.displayName = "TemplatePicker";
2136
2136
  //#endregion
2137
2137
  //#region src/shells/code-snippet-viewer.tsx
2138
- const defaultLabels$24 = {
2138
+ const defaultLabels$25 = {
2139
2139
  copy: "Copy",
2140
2140
  copied: "Copied!",
2141
2141
  codeRegion: "Code snippet"
@@ -2173,7 +2173,7 @@ const syntaxThemeClasses = {
2173
2173
  /** Renders the code snippet viewer component. */
2174
2174
  function CodeSnippetViewer({ code, language = "text", title, showLineNumbers = true, showCopyButton = true, maxHeight = 400, syntaxTheme = "default", className, labels: customLabels }) {
2175
2175
  var _syntaxThemeClasses$s;
2176
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$24), customLabels);
2176
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$25), customLabels);
2177
2177
  const [copied, setCopied] = react.useState(false);
2178
2178
  const lines = code.split("\n");
2179
2179
  const theme = (_syntaxThemeClasses$s = syntaxThemeClasses[syntaxTheme]) !== null && _syntaxThemeClasses$s !== void 0 ? _syntaxThemeClasses$s : syntaxThemeClasses.default;
@@ -2225,7 +2225,7 @@ function CodeSnippetViewer({ code, language = "text", title, showLineNumbers = t
2225
2225
  CodeSnippetViewer.displayName = "CodeSnippetViewer";
2226
2226
  //#endregion
2227
2227
  //#region src/shells/property-explorer.tsx
2228
- const defaultLabels$23 = {
2228
+ const defaultLabels$24 = {
2229
2229
  searchPlaceholder: "Search properties...",
2230
2230
  noPropertiesFound: "No properties found",
2231
2231
  edit: (property) => `${property} 편집`,
@@ -2233,7 +2233,7 @@ const defaultLabels$23 = {
2233
2233
  };
2234
2234
  /** Renders the property explorer component. */
2235
2235
  function PropertyExplorer({ groups, onSelect, onEdit, className, labels: customLabels }) {
2236
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$23), customLabels);
2236
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$24), customLabels);
2237
2237
  const [search, setSearch] = react.useState("");
2238
2238
  const filtered = react.useMemo(() => {
2239
2239
  if (!search) return groups;
@@ -2519,7 +2519,7 @@ function skipBlockComment(text, from) {
2519
2519
  }
2520
2520
  //#endregion
2521
2521
  //#region src/shells/alert-builder.tsx
2522
- const defaultLabels$22 = {
2522
+ const defaultLabels$23 = {
2523
2523
  alertNameLabel: "Alert name",
2524
2524
  conditionsLabel: "Conditions",
2525
2525
  metricLabel: "Metric",
@@ -2590,7 +2590,7 @@ function AlertBuilder({ value, onChange, metrics = [
2590
2590
  ], labels: customLabels, className }) {
2591
2591
  const resolvedMetrics = eventOptions ? eventOptions.map((e) => e.id) : metrics;
2592
2592
  const metricLabelMap = eventOptions ? Object.fromEntries(eventOptions.map((e) => [e.id, e.name])) : null;
2593
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$22), customLabels);
2593
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$23), customLabels);
2594
2594
  const alertNameId = react.useId();
2595
2595
  const conditionsId = react.useId();
2596
2596
  const checkFrequencyId = react.useId();
@@ -2745,7 +2745,7 @@ function AlertBuilder({ value, onChange, metrics = [
2745
2745
  AlertBuilder.displayName = "AlertBuilder";
2746
2746
  //#endregion
2747
2747
  //#region src/shells/event-meta-editor.tsx
2748
- const defaultLabels$21 = {
2748
+ const defaultLabels$22 = {
2749
2749
  icon: "Icon",
2750
2750
  color: "Color",
2751
2751
  name: "Event name",
@@ -2763,7 +2763,7 @@ const defaultLabels$21 = {
2763
2763
  };
2764
2764
  /** Renders the event meta editor component. */
2765
2765
  function EventMetaEditor({ value, onChange, icons, sections, footer, className, labels: customLabels }) {
2766
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$21), customLabels);
2766
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$22), customLabels);
2767
2767
  const hasTags = value.tags.length > 0;
2768
2768
  const nameId = react.useId();
2769
2769
  const displayNameId = react.useId();
@@ -2886,10 +2886,10 @@ function EventMetaEditor({ value, onChange, icons, sections, footer, className,
2886
2886
  EventMetaEditor.displayName = "EventMetaEditor";
2887
2887
  //#endregion
2888
2888
  //#region src/shells/widget-grid.tsx
2889
- const defaultLabels$20 = { emptyMessage: "No widgets to display" };
2889
+ const defaultLabels$21 = { emptyMessage: "No widgets to display" };
2890
2890
  /** Renders the widget grid component. */
2891
2891
  function WidgetGrid({ widgets, cols = 12, rowHeight = 80, gap = 16, editable = false, onReorder, labels: customLabels, className }) {
2892
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$20), customLabels);
2892
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$21), customLabels);
2893
2893
  const [dragIdx, setDragIdx] = react.useState(null);
2894
2894
  const [overIdx, setOverIdx] = react.useState(null);
2895
2895
  if (widgets.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -2934,7 +2934,7 @@ function WidgetGrid({ widgets, cols = 12, rowHeight = 80, gap = 16, editable = f
2934
2934
  WidgetGrid.displayName = "WidgetGrid";
2935
2935
  //#endregion
2936
2936
  //#region src/shells/report-builder.tsx
2937
- const defaultLabels$19 = {
2937
+ const defaultLabels$20 = {
2938
2938
  stepsLabel: "Report steps",
2939
2939
  stepType: "Chart Type",
2940
2940
  stepConfig: "Data Config",
@@ -3010,7 +3010,7 @@ const STEPS = [
3010
3010
  ];
3011
3011
  /** Renders the report builder component. */
3012
3012
  function ReportBuilder({ chartTypes = DEFAULT_CHART_TYPES, dataSources = [], onSave, onCancel, configForm, styleForm, preview, onChartTypeChange, onDataSourceChange, onStepChange, selectedChartType: controlledChartType, selectedDataSource: controlledDataSource, labels: customLabels, className }) {
3013
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$19), customLabels);
3013
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$20), customLabels);
3014
3014
  const [stepIndex, setStepIndex] = react.useState(0);
3015
3015
  const [internalChartType, setInternalChartType] = react.useState(null);
3016
3016
  const [internalDataSource, setInternalDataSource] = react.useState("");
@@ -3230,7 +3230,7 @@ function ReportBuilder({ chartTypes = DEFAULT_CHART_TYPES, dataSources = [], onS
3230
3230
  ReportBuilder.displayName = "ReportBuilder";
3231
3231
  //#endregion
3232
3232
  //#region src/shells/audit-timeline.tsx
3233
- const defaultLabels$18 = {
3233
+ const defaultLabels$19 = {
3234
3234
  title: "변경 이력",
3235
3235
  empty: "변경 이력이 없습니다",
3236
3236
  expand: "상세 보기",
@@ -3309,7 +3309,7 @@ function DiffTable({ changes, labels }) {
3309
3309
  }
3310
3310
  /** Renders the audit timeline component. */
3311
3311
  function AuditTimeline({ entries, labels: customLabels, className, onEntryClick }) {
3312
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$18), customLabels);
3312
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$19), customLabels);
3313
3313
  const [expandedIds, setExpandedIds] = react.useState(/* @__PURE__ */ new Set());
3314
3314
  const toggleExpanded = react.useCallback((id) => {
3315
3315
  setExpandedIds((prev) => {
@@ -3432,7 +3432,7 @@ function AuditTimeline({ entries, labels: customLabels, className, onEntryClick
3432
3432
  AuditTimeline.displayName = "AuditTimeline";
3433
3433
  //#endregion
3434
3434
  //#region src/shells/segment-builder.tsx
3435
- const defaultLabels$17 = {
3435
+ const defaultLabels$18 = {
3436
3436
  match: "Match",
3437
3437
  ofTheFollowing: "of the following",
3438
3438
  groupLabel: "Segment group",
@@ -3553,7 +3553,7 @@ function SegmentBuilder({ groups, onChange, eventOptions = [
3553
3553
  "New Users",
3554
3554
  "Power Users"
3555
3555
  ], className, labels: customLabels }) {
3556
- const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$17), customLabels);
3556
+ const labels = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$18), customLabels);
3557
3557
  const builderId = react.useId().replace(/:/g, "");
3558
3558
  const nextIdRef = react.useRef(1);
3559
3559
  const createId = react.useCallback(() => {
@@ -3765,6 +3765,703 @@ function SegmentBuilder({ groups, onChange, eventOptions = [
3765
3765
  }
3766
3766
  SegmentBuilder.displayName = "SegmentBuilder";
3767
3767
  //#endregion
3768
+ //#region src/shells/criteria-builder.tsx
3769
+ const defaultLabels$17 = {
3770
+ builder: "조건 빌더",
3771
+ rootLogicLegend: "그룹 결합 방식",
3772
+ matchAllGroups: "모든 그룹 일치",
3773
+ matchAnyGroup: "하나 이상 일치",
3774
+ groupLogicLegend: (letter) => `그룹 ${letter} 조건 결합 방식`,
3775
+ matchAllCriteria: "모두 충족",
3776
+ matchAnyCriteria: "하나 이상 충족",
3777
+ and: "그리고",
3778
+ or: "또는",
3779
+ groupLabel: (letter) => `그룹 ${letter}`,
3780
+ criterionLabel: (letter, index) => `조건 ${letter}.${index}`,
3781
+ actionLabel: (action, target) => `${action}: ${target}`,
3782
+ addCriterion: "조건 추가",
3783
+ addGroup: "그룹 추가",
3784
+ kindMenuTitle: "조건 유형",
3785
+ duplicateCriterion: "조건 복제",
3786
+ removeCriterion: "조건 제거",
3787
+ moveUp: "위로 이동",
3788
+ moveDown: "아래로 이동",
3789
+ negate: "제외",
3790
+ duplicateGroup: "그룹 복제",
3791
+ removeGroup: "그룹 제거",
3792
+ emptyGroup: "이 그룹에는 아직 조건이 없습니다.",
3793
+ unknownKind: (kind) => `알 수 없는 조건 유형 "${kind}"`,
3794
+ unknownKindDescription: "이 조건은 표시할 수 없습니다. 유형을 바꾸거나 제거하세요.",
3795
+ replaceKind: "유형 바꾸기",
3796
+ emptyTitle: "조건이 없습니다",
3797
+ emptyDescription: "첫 그룹을 추가해 누가 포함되는지 정의하세요.",
3798
+ emptyAction: "첫 조건 추가",
3799
+ keyboardHint: "조건 추가 · 그룹 추가",
3800
+ maxGroupsReached: (max) => `그룹은 최대 ${max}개까지 추가할 수 있습니다.`,
3801
+ maxCriteriaReached: (max) => `한 그룹에는 조건을 최대 ${max}개까지 추가할 수 있습니다.`
3802
+ };
3803
+ /** `A`, `B`, … `Z`, `AA`, `AB` — the badge a group wears. */
3804
+ function criteriaGroupLetter(index) {
3805
+ let n = Math.max(0, Math.floor(index));
3806
+ let out = "";
3807
+ do {
3808
+ out = String.fromCharCode(65 + n % 26) + out;
3809
+ n = Math.floor(n / 26) - 1;
3810
+ } while (n >= 0);
3811
+ return out;
3812
+ }
3813
+ function collectIds(value) {
3814
+ const ids = /* @__PURE__ */ new Set();
3815
+ for (const group of value.groups) {
3816
+ ids.add(group.id);
3817
+ for (const criterion of group.criteria) ids.add(criterion.id);
3818
+ }
3819
+ return ids;
3820
+ }
3821
+ function uniqueId(base, taken) {
3822
+ if (!taken.has(base)) return base;
3823
+ let n = 2;
3824
+ while (taken.has(`${base}-${n}`)) n += 1;
3825
+ return `${base}-${n}`;
3826
+ }
3827
+ function replaceGroup(value, groupIndex, next) {
3828
+ return require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups: value.groups.map((group, i) => i === groupIndex ? next : group) });
3829
+ }
3830
+ /**
3831
+ * Moves one criterion a step up or down inside its group. Returns the same
3832
+ * value when the move is impossible, so a caller can `===` to know nothing
3833
+ * changed.
3834
+ */
3835
+ function criteriaBuilderMove(value, groupIndex, index, direction) {
3836
+ const group = value.groups[groupIndex];
3837
+ if (!group) return value;
3838
+ const target = direction === "up" ? index - 1 : index + 1;
3839
+ if (index < 0 || target < 0 || target >= group.criteria.length) return value;
3840
+ const criteria = [...group.criteria];
3841
+ const [moved] = criteria.splice(index, 1);
3842
+ criteria.splice(target, 0, moved);
3843
+ return replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria }));
3844
+ }
3845
+ /**
3846
+ * Inserts a copy of one criterion *before* the original, so the copy takes the
3847
+ * original's place in the reading order and the original follows as the thing
3848
+ * to edit.
3849
+ */
3850
+ function criteriaBuilderDuplicate(value, groupIndex, index, clone) {
3851
+ const group = value.groups[groupIndex];
3852
+ const original = group === null || group === void 0 ? void 0 : group.criteria[index];
3853
+ if (!group || !original) return value;
3854
+ const criteria = [...group.criteria];
3855
+ criteria.splice(index, 0, clone(original));
3856
+ return replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria }));
3857
+ }
3858
+ /** Removes one criterion. The group stays, even when it empties. */
3859
+ function criteriaBuilderRemove(value, groupIndex, index) {
3860
+ const group = value.groups[groupIndex];
3861
+ if (!group || index < 0 || index >= group.criteria.length) return value;
3862
+ return replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria: group.criteria.filter((_, i) => i !== index) }));
3863
+ }
3864
+ /** Appends a criterion to a group, or inserts it at `at`. */
3865
+ function criteriaBuilderInsert(value, groupIndex, criterion, at) {
3866
+ const group = value.groups[groupIndex];
3867
+ if (!group) return value;
3868
+ const criteria = [...group.criteria];
3869
+ const position = at === void 0 ? criteria.length : Math.max(0, Math.min(at, criteria.length));
3870
+ criteria.splice(position, 0, criterion);
3871
+ return replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria }));
3872
+ }
3873
+ /** Removes one group. */
3874
+ function criteriaBuilderRemoveGroup(value, groupIndex) {
3875
+ if (groupIndex < 0 || groupIndex >= value.groups.length) return value;
3876
+ return require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups: value.groups.filter((_, i) => i !== groupIndex) });
3877
+ }
3878
+ /** Inserts a copy of one group — every criterion cloned — before the original. */
3879
+ function criteriaBuilderDuplicateGroup(value, groupIndex, groupId, clone) {
3880
+ const group = value.groups[groupIndex];
3881
+ if (!group) return value;
3882
+ const copy = {
3883
+ id: groupId,
3884
+ logic: group.logic,
3885
+ criteria: group.criteria.map(clone)
3886
+ };
3887
+ const groups = [...value.groups];
3888
+ groups.splice(groupIndex, 0, copy);
3889
+ return require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups });
3890
+ }
3891
+ const EDITABLE_TARGET = "input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"], [role=\"combobox\"], [role=\"textbox\"], [role=\"menu\"], [role=\"menuitem\"], [role=\"listbox\"], [role=\"option\"]";
3892
+ function isEditableTarget(target) {
3893
+ return target instanceof Element && target.closest(EDITABLE_TARGET) !== null;
3894
+ }
3895
+ function sectionKinds(kinds) {
3896
+ const sections = [];
3897
+ const byHeading = /* @__PURE__ */ new Map();
3898
+ for (const option of kinds) {
3899
+ let section = byHeading.get(option.group);
3900
+ if (!section) {
3901
+ section = {
3902
+ heading: option.group,
3903
+ entries: []
3904
+ };
3905
+ byHeading.set(option.group, section);
3906
+ sections.push(section);
3907
+ }
3908
+ section.entries.push({
3909
+ key: option.kind,
3910
+ kind: option.kind,
3911
+ negate: false,
3912
+ option
3913
+ });
3914
+ if (option.negatedLabel) section.entries.push({
3915
+ key: `${option.kind}:not`,
3916
+ kind: option.kind,
3917
+ negate: true,
3918
+ option
3919
+ });
3920
+ }
3921
+ return [...sections.filter((s) => s.heading === void 0), ...sections.filter((s) => s.heading !== void 0)];
3922
+ }
3923
+ function KindMenu({ kinds, menuLabel, triggerLabel, triggerAriaLabel, triggerIcon, disabled = false, disabledReason, variant = "ghost", onPick, addTarget }) {
3924
+ const sections = react.useMemo(() => sectionKinds(kinds), [kinds]);
3925
+ const trigger = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownTrigger, {
3926
+ disabled,
3927
+ "aria-label": triggerAriaLabel,
3928
+ title: disabled ? disabledReason : void 0,
3929
+ "data-criteria-add": addTarget,
3930
+ className: require_text_truncate.cn("inline-flex cursor-pointer items-center justify-center rounded-lg font-medium transition-colors", "h-[var(--opt-control-sm)] gap-[var(--opt-space-1-5)] px-[var(--opt-control-padding-sm)] text-xs", variant === "secondary" ? "border-border bg-surface text-text-primary hover:bg-bg-subtle active:bg-bg-muted border" : "text-text-secondary hover:bg-bg-subtle active:bg-bg-muted", "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", "[&_svg]:size-3.5", require_text_truncate.OPT_FOCUS_VISIBLE),
3931
+ children: [triggerIcon, triggerLabel]
3932
+ });
3933
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownMenu, {
3934
+ animated: false,
3935
+ children: [trigger, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownContent, {
3936
+ "aria-label": menuLabel,
3937
+ "aria-labelledby": void 0,
3938
+ className: "min-w-56",
3939
+ children: sections.map((section, si) => {
3940
+ var _section$heading;
3941
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [
3942
+ si > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.DropdownSeparator, {}),
3943
+ section.heading !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3944
+ role: "presentation",
3945
+ className: "text-text-tertiary px-2 pt-1.5 pb-1 text-[11px] font-semibold tracking-wide uppercase",
3946
+ children: section.heading
3947
+ }),
3948
+ section.entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_text_truncate.DropdownItem, {
3949
+ onClick: () => onPick(entry.kind, entry.negate),
3950
+ className: "flex items-start gap-2",
3951
+ children: [entry.option.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3952
+ className: "text-text-tertiary mt-0.5 shrink-0 [&_svg]:size-3.5",
3953
+ children: entry.option.icon
3954
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3955
+ className: "flex min-w-0 flex-col",
3956
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3957
+ className: require_text_truncate.cn("text-sm", entry.negate && "text-danger-fg"),
3958
+ children: entry.negate ? entry.option.negatedLabel : entry.option.label
3959
+ }), !entry.negate && entry.option.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3960
+ className: "text-text-tertiary text-xs",
3961
+ children: entry.option.description
3962
+ })]
3963
+ })]
3964
+ }, entry.key))
3965
+ ] }, (_section$heading = section.heading) !== null && _section$heading !== void 0 ? _section$heading : "__none");
3966
+ })
3967
+ })]
3968
+ });
3969
+ }
3970
+ function IconAction({ icon, label, onClick, disabled = false, danger = false }) {
3971
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3972
+ type: "button",
3973
+ onClick,
3974
+ disabled,
3975
+ "aria-label": label,
3976
+ title: label,
3977
+ className: require_text_truncate.cn("inline-flex size-7 shrink-0 items-center justify-center rounded-md transition-colors [&_svg]:size-3.5", danger ? "text-text-tertiary hover:bg-danger-subtle hover:text-danger-fg" : "text-text-tertiary hover:bg-bg-muted hover:text-text-primary", "disabled:pointer-events-none disabled:opacity-40", require_text_truncate.OPT_FOCUS_VISIBLE),
3978
+ children: icon
3979
+ });
3980
+ }
3981
+ function LogicDivider({ label, emphasis }) {
3982
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3983
+ role: "separator",
3984
+ "aria-label": label,
3985
+ className: require_text_truncate.cn("flex items-center", emphasis === "root" ? "gap-group" : "gap-element"),
3986
+ children: [
3987
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: require_text_truncate.cn("h-px flex-1", emphasis === "root" ? "bg-border" : "bg-border-subtle") }),
3988
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3989
+ className: require_text_truncate.cn("text-[11px] font-semibold tracking-wide uppercase", emphasis === "root" ? "bg-bg-subtle text-text-secondary border-border rounded-full border px-2 py-0.5" : "text-text-tertiary"),
3990
+ children: label
3991
+ }),
3992
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: require_text_truncate.cn("h-px flex-1", emphasis === "root" ? "bg-border" : "bg-border-subtle") })
3993
+ ]
3994
+ });
3995
+ }
3996
+ function Lettermark({ letter }) {
3997
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3998
+ "aria-hidden": "true",
3999
+ className: "bg-accent-subtle text-accent inline-flex size-6 shrink-0 items-center justify-center rounded-md text-xs font-semibold tabular-nums",
4000
+ children: letter
4001
+ });
4002
+ }
4003
+ /**
4004
+ * CriteriaBuilder — a two-level tree of criteria, edited as sentences.
4005
+ *
4006
+ * The builder owns what every criteria editor needs and nothing a particular
4007
+ * product does: groups and their AND/OR, the order of criteria, negation,
4008
+ * inline errors, the "add" menu, and the keyboard. The consumer owns the
4009
+ * criterion type and renders each one — usually as `Expression` chips whose
4010
+ * values open an editor — so the same shell serves a cohort of users, an
4011
+ * alert condition, or a feature-flag rollout without knowing which.
4012
+ *
4013
+ * Kinds the builder does not know render as an invalid row with a warning and
4014
+ * a replace/remove pair, never as some other kind's fields: a criterion saved
4015
+ * by a newer client should look broken, not silently different.
4016
+ *
4017
+ * @example
4018
+ * ```tsx
4019
+ * <CriteriaBuilder
4020
+ * value={value}
4021
+ * onChange={setValue}
4022
+ * kinds={[
4023
+ * { kind: "performed", label: "이벤트를 수행함", negatedLabel: "이벤트를 수행하지 않음" },
4024
+ * { kind: "property", label: "속성이 일치함" },
4025
+ * ]}
4026
+ * createCriterion={(kind, negate) => ({ id: nextId(), kind, negate, event: "" })}
4027
+ * renderCriterion={(c, api) => (
4028
+ * <ExpressionGroup>
4029
+ * <Expression description="이벤트" value={c.event} onClick={() => open(c.id)} />
4030
+ * </ExpressionGroup>
4031
+ * )}
4032
+ * />
4033
+ * ```
4034
+ */
4035
+ function CriteriaBuilder({ value, onChange, kinds, createCriterion, renderCriterion, cloneCriterion, createGroupId, errors, maxGroups = 10, maxCriteriaPerGroup = 20, allowNegation = true, disabled = false, aside, labels: customLabels, className, "aria-label": ariaLabel }) {
4036
+ const labels = react.useMemo(() => require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, defaultLabels$17), customLabels), [customLabels]);
4037
+ const baseId = react.useId().replace(/:/g, "");
4038
+ const nextGroupRef = react.useRef(1);
4039
+ const rootRef = react.useRef(null);
4040
+ const [pendingOpen, setPendingOpen] = react.useState(null);
4041
+ const knownKinds = react.useMemo(() => new Set(kinds.map((k) => k.kind)), [kinds]);
4042
+ const groupsFull = value.groups.length >= maxGroups;
4043
+ const newGroupId = react.useCallback(() => {
4044
+ const taken = collectIds(value);
4045
+ if (createGroupId) return uniqueId(createGroupId(), taken);
4046
+ let id = "";
4047
+ do
4048
+ id = `group-${nextGroupRef.current++}`;
4049
+ while (taken.has(id));
4050
+ return id;
4051
+ }, [createGroupId, value]);
4052
+ const clone = react.useCallback((criterion, taken) => {
4053
+ if (cloneCriterion) return cloneCriterion(criterion);
4054
+ const id = uniqueId(`${criterion.id}-copy`, taken);
4055
+ taken.add(id);
4056
+ return require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, criterion), {}, { id });
4057
+ }, [cloneCriterion]);
4058
+ const addGroup = react.useCallback(() => {
4059
+ if (disabled || groupsFull) return null;
4060
+ const id = newGroupId();
4061
+ onChange(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups: [...value.groups, {
4062
+ id,
4063
+ logic: "and",
4064
+ criteria: []
4065
+ }] }));
4066
+ return id;
4067
+ }, [
4068
+ disabled,
4069
+ groupsFull,
4070
+ newGroupId,
4071
+ onChange,
4072
+ value
4073
+ ]);
4074
+ const addCriterion = react.useCallback((groupIndex, kind, negate) => {
4075
+ const group = value.groups[groupIndex];
4076
+ if (!group || group.criteria.length >= maxCriteriaPerGroup) return;
4077
+ onChange(criteriaBuilderInsert(value, groupIndex, createCriterion(kind, negate)));
4078
+ }, [
4079
+ createCriterion,
4080
+ maxCriteriaPerGroup,
4081
+ onChange,
4082
+ value
4083
+ ]);
4084
+ const addFirst = react.useCallback((kind, negate) => {
4085
+ if (disabled) return;
4086
+ onChange(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { groups: [{
4087
+ id: newGroupId(),
4088
+ logic: "and",
4089
+ criteria: [createCriterion(kind, negate)]
4090
+ }] }));
4091
+ }, [
4092
+ createCriterion,
4093
+ disabled,
4094
+ newGroupId,
4095
+ onChange,
4096
+ value
4097
+ ]);
4098
+ const replaceCriterion = react.useCallback((groupIndex, index, kind, negate) => {
4099
+ const group = value.groups[groupIndex];
4100
+ const current = group === null || group === void 0 ? void 0 : group.criteria[index];
4101
+ if (!group || !current) return;
4102
+ const replacement = require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, createCriterion(kind, negate)), {}, { id: current.id });
4103
+ onChange(replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria: group.criteria.map((c, i) => i === index ? replacement : c) })));
4104
+ }, [
4105
+ createCriterion,
4106
+ onChange,
4107
+ value
4108
+ ]);
4109
+ const updateCriterion = react.useCallback((groupIndex, index, patch) => {
4110
+ const group = value.groups[groupIndex];
4111
+ if (!group) return;
4112
+ onChange(replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { criteria: group.criteria.map((c, i) => i === index ? require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, c), patch) : c) })));
4113
+ }, [onChange, value]);
4114
+ const findAddTrigger = react.useCallback((groupId) => {
4115
+ var _rootRef$current$quer, _rootRef$current;
4116
+ return (_rootRef$current$quer = (_rootRef$current = rootRef.current) === null || _rootRef$current === void 0 ? void 0 : _rootRef$current.querySelector(`[data-criteria-add="${groupId.replace(/["\\]/g, "\\$&")}"]`)) !== null && _rootRef$current$quer !== void 0 ? _rootRef$current$quer : null;
4117
+ }, []);
4118
+ react.useEffect(() => {
4119
+ if (!pendingOpen) return;
4120
+ const trigger = findAddTrigger(pendingOpen);
4121
+ if (!trigger) return;
4122
+ setPendingOpen(null);
4123
+ trigger.focus();
4124
+ trigger.click();
4125
+ }, [
4126
+ findAddTrigger,
4127
+ pendingOpen,
4128
+ value
4129
+ ]);
4130
+ const handleKeyDown = react.useCallback((event) => {
4131
+ if (disabled || event.defaultPrevented) return;
4132
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
4133
+ if (isEditableTarget(event.target)) return;
4134
+ const key = event.key.toLowerCase();
4135
+ if (key === "g") {
4136
+ const id = addGroup();
4137
+ if (id) {
4138
+ event.preventDefault();
4139
+ setPendingOpen(id);
4140
+ }
4141
+ return;
4142
+ }
4143
+ if (key === "a") {
4144
+ const host = event.target instanceof Element ? event.target.closest("[data-criteria-group]") : null;
4145
+ const groupId = host === null || host === void 0 ? void 0 : host.dataset.criteriaGroup;
4146
+ const trigger = groupId ? findAddTrigger(groupId) : null;
4147
+ if (trigger && !trigger.disabled) {
4148
+ event.preventDefault();
4149
+ trigger.focus();
4150
+ trigger.click();
4151
+ }
4152
+ }
4153
+ }, [
4154
+ addGroup,
4155
+ disabled,
4156
+ findAddTrigger
4157
+ ]);
4158
+ const rootLogicOptions = react.useMemo(() => [{
4159
+ value: "and",
4160
+ label: labels.matchAllGroups
4161
+ }, {
4162
+ value: "or",
4163
+ label: labels.matchAnyGroup
4164
+ }], [labels]);
4165
+ const groupLogicOptions = react.useMemo(() => [{
4166
+ value: "and",
4167
+ label: labels.matchAllCriteria
4168
+ }, {
4169
+ value: "or",
4170
+ label: labels.matchAnyCriteria
4171
+ }], [labels]);
4172
+ const isEmpty = value.groups.length === 0;
4173
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4174
+ ref: rootRef,
4175
+ role: "group",
4176
+ "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : labels.builder,
4177
+ className: require_text_truncate.cn("gap-section flex flex-col lg:flex-row", className),
4178
+ onKeyDown: handleKeyDown,
4179
+ "data-opt-id": "6F4JG",
4180
+ "data-opt-slug": "criteria-builder.CriteriaBuilder",
4181
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4182
+ className: "gap-group flex min-w-0 flex-1 flex-col",
4183
+ children: isEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.EmptyState, {
4184
+ title: labels.emptyTitle,
4185
+ description: labels.emptyDescription,
4186
+ hasBorder: true,
4187
+ tone: "subdued",
4188
+ actions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KindMenu, {
4189
+ kinds,
4190
+ menuLabel: labels.kindMenuTitle,
4191
+ triggerLabel: labels.emptyAction,
4192
+ triggerAriaLabel: labels.emptyAction,
4193
+ triggerIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { "aria-hidden": "true" }),
4194
+ variant: "secondary",
4195
+ disabled,
4196
+ onPick: addFirst
4197
+ }),
4198
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4199
+ className: "gap-element inline-flex items-center",
4200
+ children: [
4201
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Kbd, {
4202
+ hint: true,
4203
+ children: "A"
4204
+ }),
4205
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Kbd, {
4206
+ hint: true,
4207
+ children: "G"
4208
+ }),
4209
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: labels.keyboardHint })
4210
+ ]
4211
+ })
4212
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
4214
+ className: "gap-group m-0 flex min-w-0 flex-wrap items-center border-0 p-0",
4215
+ children: [
4216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
4217
+ className: "sr-only",
4218
+ children: labels.rootLogicLegend
4219
+ }),
4220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.SegmentedControl, {
4221
+ "aria-label": labels.rootLogicLegend,
4222
+ size: "sm",
4223
+ options: rootLogicOptions,
4224
+ value: value.logic,
4225
+ onChange: (logic) => onChange(require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, value), {}, { logic })),
4226
+ disabled
4227
+ }),
4228
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4229
+ className: "text-text-tertiary gap-element hidden items-center text-xs sm:inline-flex",
4230
+ children: [
4231
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Kbd, {
4232
+ hint: true,
4233
+ children: "A"
4234
+ }),
4235
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Kbd, {
4236
+ hint: true,
4237
+ children: "G"
4238
+ }),
4239
+ labels.keyboardHint
4240
+ ]
4241
+ })
4242
+ ]
4243
+ }),
4244
+ value.groups.map((group, groupIndex) => {
4245
+ const letter = criteriaGroupLetter(groupIndex);
4246
+ const groupName = labels.groupLabel(letter);
4247
+ const groupLabelId = `${baseId}-${group.id}-label`;
4248
+ const groupError = errors === null || errors === void 0 ? void 0 : errors[group.id];
4249
+ const groupErrorId = `${baseId}-${group.id}-error`;
4250
+ const criteriaFull = group.criteria.length >= maxCriteriaPerGroup;
4251
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [groupIndex > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LogicDivider, {
4252
+ emphasis: "root",
4253
+ label: value.logic === "and" ? labels.and : labels.or
4254
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4255
+ role: "group",
4256
+ "aria-labelledby": groupLabelId,
4257
+ "aria-describedby": groupError ? groupErrorId : void 0,
4258
+ "data-criteria-group": group.id,
4259
+ "data-invalid": groupError ? true : void 0,
4260
+ className: require_text_truncate.cn("bg-bg-subtle gap-element flex flex-col rounded-lg border p-3", groupError ? "border-danger/40" : "border-border"),
4261
+ children: [
4262
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4263
+ id: groupLabelId,
4264
+ className: "sr-only",
4265
+ children: groupName
4266
+ }),
4267
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4268
+ className: "gap-element flex flex-wrap items-center justify-between",
4269
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4270
+ className: "gap-element flex items-center",
4271
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lettermark, { letter }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
4272
+ className: "m-0 border-0 p-0",
4273
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
4274
+ className: "sr-only",
4275
+ children: labels.groupLogicLegend(letter)
4276
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.SegmentedControl, {
4277
+ "aria-label": labels.groupLogicLegend(letter),
4278
+ size: "sm",
4279
+ options: groupLogicOptions,
4280
+ value: group.logic,
4281
+ onChange: (logic) => onChange(replaceGroup(value, groupIndex, require_text_truncate._objectSpread2(require_text_truncate._objectSpread2({}, group), {}, { logic }))),
4282
+ disabled
4283
+ })]
4284
+ })]
4285
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4286
+ className: "flex items-center gap-0.5",
4287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4288
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Copy, { "aria-hidden": "true" }),
4289
+ label: labels.actionLabel(labels.duplicateGroup, groupName),
4290
+ disabled: disabled || groupsFull,
4291
+ onClick: () => {
4292
+ const taken = collectIds(value);
4293
+ const id = newGroupId();
4294
+ taken.add(id);
4295
+ onChange(criteriaBuilderDuplicateGroup(value, groupIndex, id, (c) => clone(c, taken)));
4296
+ }
4297
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4298
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Trash2, { "aria-hidden": "true" }),
4299
+ label: labels.actionLabel(labels.removeGroup, groupName),
4300
+ disabled,
4301
+ danger: true,
4302
+ onClick: () => onChange(criteriaBuilderRemoveGroup(value, groupIndex))
4303
+ })]
4304
+ })]
4305
+ }),
4306
+ group.criteria.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4307
+ className: "text-text-tertiary text-xs",
4308
+ children: labels.emptyGroup
4309
+ }),
4310
+ group.criteria.map((criterion, index) => {
4311
+ const criterionName = labels.criterionLabel(letter, index + 1);
4312
+ const rowLabelId = `${baseId}-${criterion.id}-label`;
4313
+ const error = errors === null || errors === void 0 ? void 0 : errors[criterion.id];
4314
+ const errorId = `${baseId}-${criterion.id}-error`;
4315
+ const known = knownKinds.has(criterion.kind);
4316
+ const negated = criterion.negate === true;
4317
+ const api = {
4318
+ update: (patch) => updateCriterion(groupIndex, index, patch),
4319
+ invalid: Boolean(error) || !known,
4320
+ error,
4321
+ disabled,
4322
+ groupIndex,
4323
+ index
4324
+ };
4325
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [index > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LogicDivider, {
4326
+ emphasis: "group",
4327
+ label: group.logic === "and" ? labels.and : labels.or
4328
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4329
+ role: "group",
4330
+ "aria-labelledby": rowLabelId,
4331
+ "aria-describedby": error ? errorId : void 0,
4332
+ "data-criterion-kind": criterion.kind,
4333
+ "data-negated": negated || void 0,
4334
+ "data-invalid": error || !known ? true : void 0,
4335
+ className: require_text_truncate.cn("bg-surface gap-element flex flex-col rounded-md border p-2", error || !known ? "border-danger/40" : "border-border-subtle"),
4336
+ children: [
4337
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4338
+ id: rowLabelId,
4339
+ className: "sr-only",
4340
+ children: criterionName
4341
+ }),
4342
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4343
+ className: "gap-element flex flex-wrap items-start",
4344
+ children: [
4345
+ allowNegation && known && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4346
+ type: "button",
4347
+ "aria-pressed": negated,
4348
+ "aria-label": labels.actionLabel(labels.negate, criterionName),
4349
+ disabled,
4350
+ onClick: () => updateCriterion(groupIndex, index, { negate: !negated }),
4351
+ className: require_text_truncate.cn("inline-flex h-7 shrink-0 items-center rounded-md border px-2 text-[11px] font-semibold tracking-wide uppercase transition-colors", negated ? "border-danger/40 bg-danger-subtle text-danger-fg" : "border-border bg-bg-subtle text-text-tertiary hover:text-text-secondary", "disabled:pointer-events-none disabled:opacity-50", "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset"),
4352
+ children: labels.negate
4353
+ }),
4354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4355
+ className: "min-w-0 flex-1",
4356
+ children: known ? renderCriterion(criterion, api) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_field_sidebar.Callout, {
4357
+ tone: "warning",
4358
+ size: "sm",
4359
+ title: labels.unknownKind(criterion.kind),
4360
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.TriangleAlert, { "aria-hidden": "true" }),
4361
+ action: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KindMenu, {
4362
+ kinds,
4363
+ menuLabel: labels.kindMenuTitle,
4364
+ triggerLabel: labels.replaceKind,
4365
+ triggerAriaLabel: labels.actionLabel(labels.replaceKind, criterionName),
4366
+ variant: "secondary",
4367
+ disabled,
4368
+ onPick: (kind, negate) => replaceCriterion(groupIndex, index, kind, negate)
4369
+ }),
4370
+ children: labels.unknownKindDescription
4371
+ })
4372
+ }),
4373
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4374
+ className: "flex items-center gap-0.5",
4375
+ children: [
4376
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4377
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { "aria-hidden": "true" }),
4378
+ label: labels.actionLabel(labels.moveUp, criterionName),
4379
+ disabled: disabled || index === 0,
4380
+ onClick: () => onChange(criteriaBuilderMove(value, groupIndex, index, "up"))
4381
+ }),
4382
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4383
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowDown, { "aria-hidden": "true" }),
4384
+ label: labels.actionLabel(labels.moveDown, criterionName),
4385
+ disabled: disabled || index === group.criteria.length - 1,
4386
+ onClick: () => onChange(criteriaBuilderMove(value, groupIndex, index, "down"))
4387
+ }),
4388
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4389
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Copy, { "aria-hidden": "true" }),
4390
+ label: labels.actionLabel(labels.duplicateCriterion, criterionName),
4391
+ disabled: disabled || criteriaFull,
4392
+ onClick: () => onChange(criteriaBuilderDuplicate(value, groupIndex, index, (c) => clone(c, collectIds(value))))
4393
+ }),
4394
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAction, {
4395
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Trash2, { "aria-hidden": "true" }),
4396
+ label: labels.actionLabel(labels.removeCriterion, criterionName),
4397
+ disabled,
4398
+ danger: true,
4399
+ onClick: () => onChange(criteriaBuilderRemove(value, groupIndex, index))
4400
+ })
4401
+ ]
4402
+ })
4403
+ ]
4404
+ }),
4405
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4406
+ id: errorId,
4407
+ className: "text-danger-fg text-xs",
4408
+ children: error
4409
+ })
4410
+ ]
4411
+ })] }, criterion.id);
4412
+ }),
4413
+ groupError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4414
+ id: groupErrorId,
4415
+ className: "text-danger-fg text-xs",
4416
+ children: groupError
4417
+ }),
4418
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4419
+ className: "gap-element flex flex-wrap items-center",
4420
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(KindMenu, {
4421
+ kinds,
4422
+ menuLabel: labels.kindMenuTitle,
4423
+ triggerLabel: labels.addCriterion,
4424
+ triggerAriaLabel: labels.actionLabel(labels.addCriterion, groupName),
4425
+ triggerIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { "aria-hidden": "true" }),
4426
+ disabled: disabled || criteriaFull,
4427
+ disabledReason: labels.maxCriteriaReached(maxCriteriaPerGroup),
4428
+ onPick: (kind, negate) => addCriterion(groupIndex, kind, negate),
4429
+ addTarget: group.id
4430
+ }), criteriaFull && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4431
+ className: "text-text-tertiary text-xs",
4432
+ children: labels.maxCriteriaReached(maxCriteriaPerGroup)
4433
+ })]
4434
+ })
4435
+ ]
4436
+ })] }, group.id);
4437
+ }),
4438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4439
+ className: "gap-element flex flex-wrap items-center",
4440
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_text_truncate.Button, {
4441
+ variant: "secondary",
4442
+ size: "sm",
4443
+ leftIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { "aria-hidden": "true" }),
4444
+ disabled: disabled || groupsFull,
4445
+ title: groupsFull ? labels.maxGroupsReached(maxGroups) : void 0,
4446
+ onClick: () => {
4447
+ const id = addGroup();
4448
+ if (id) setPendingOpen(id);
4449
+ },
4450
+ children: labels.addGroup
4451
+ }), groupsFull && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4452
+ className: "text-text-tertiary text-xs",
4453
+ children: labels.maxGroupsReached(maxGroups)
4454
+ })]
4455
+ })
4456
+ ] })
4457
+ }), aside !== void 0 && aside !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
4458
+ className: "min-w-0 lg:w-72 lg:shrink-0",
4459
+ children: aside
4460
+ })]
4461
+ });
4462
+ }
4463
+ CriteriaBuilder.displayName = "CriteriaBuilder";
4464
+ //#endregion
3768
4465
  //#region src/shells/insights-panel.tsx
3769
4466
  const defaultLabels$16 = {
3770
4467
  title: "Insights",
@@ -8253,6 +8950,7 @@ exports.ConnectionIndicator = ConnectionIndicator;
8253
8950
  exports.Container = require_key_pad_menu.Container;
8254
8951
  exports.ContentTabs = require_field_sidebar.ContentTabs;
8255
8952
  exports.CopyButton = CopyButton;
8953
+ exports.CriteriaBuilder = CriteriaBuilder;
8256
8954
  exports.DEFAULT_PRESETS = require_text_truncate.DEFAULT_PRESETS;
8257
8955
  exports.DEFAULT_TIME_PRESETS = require_field_sidebar.DEFAULT_TIME_PRESETS;
8258
8956
  exports.DashboardGrid = require_field_sidebar.DashboardGrid;
@@ -8524,6 +9222,13 @@ exports.countActiveQueryFilters = require_field_sidebar.countActiveQueryFilters;
8524
9222
  exports.createBlock = require_key_pad_menu.createBlock;
8525
9223
  exports.createSurface = require_key_pad_menu.createBlock;
8526
9224
  exports.createThemeBootScript = require_field_sidebar.createThemeBootScript;
9225
+ exports.criteriaBuilderDuplicate = criteriaBuilderDuplicate;
9226
+ exports.criteriaBuilderDuplicateGroup = criteriaBuilderDuplicateGroup;
9227
+ exports.criteriaBuilderInsert = criteriaBuilderInsert;
9228
+ exports.criteriaBuilderMove = criteriaBuilderMove;
9229
+ exports.criteriaBuilderRemove = criteriaBuilderRemove;
9230
+ exports.criteriaBuilderRemoveGroup = criteriaBuilderRemoveGroup;
9231
+ exports.criteriaGroupLetter = criteriaGroupLetter;
8527
9232
  exports.defaultQueryChartConfig = require_field_sidebar.defaultQueryChartConfig;
8528
9233
  exports.defineThemeOverrides = require_field_sidebar.defineThemeOverrides;
8529
9234
  exports.describeClause = require_field_sidebar.describeClause;