@wipco/sdui 1.6.0 → 1.7.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 (98) hide show
  1. package/dist/{DatePicker-DX9juElE.d.ts → DatePicker-BOOKdd5x.d.ts} +6 -6
  2. package/dist/calendar-nav-BSGsgJc4.d.ts +54 -0
  3. package/dist/{chunk-53KS4E5N.js → chunk-27ILQUWE.js} +121 -28
  4. package/dist/{chunk-AY4GM6KQ.js → chunk-466W3XNK.js} +1 -1
  5. package/dist/{chunk-X2NIRVLN.js → chunk-4VQIKSDS.js} +157 -190
  6. package/dist/{chunk-LIZTK3HO.js → chunk-5FZBH3T5.js} +1 -1
  7. package/dist/{chunk-NA232CBX.js → chunk-5LIFU57A.js} +2 -2
  8. package/dist/{chunk-LKQJJSVJ.js → chunk-63GM4S25.js} +1 -1
  9. package/dist/{chunk-CSE3PZOZ.js → chunk-7EDFYBUA.js} +4 -4
  10. package/dist/{chunk-MN4RZ2WP.js → chunk-7YTZB5LQ.js} +3 -3
  11. package/dist/{chunk-WEG3V7ZB.js → chunk-7ZCM6XEM.js} +2 -2
  12. package/dist/{chunk-6XI2VF2O.js → chunk-A22QR6PM.js} +2 -2
  13. package/dist/{chunk-CEVZAFPO.js → chunk-A3SO6H47.js} +2 -2
  14. package/dist/{chunk-2KQL6DYG.js → chunk-AP33B24H.js} +65 -9
  15. package/dist/{chunk-FEKKOBPX.js → chunk-B5MB64YQ.js} +209 -25
  16. package/dist/{chunk-IQXLNICY.js → chunk-DCSUBFFG.js} +4 -4
  17. package/dist/{chunk-OUEJWC2Z.js → chunk-DILQ2DJV.js} +5 -5
  18. package/dist/{chunk-NAIWQQPW.js → chunk-ENZBX4AR.js} +1 -1
  19. package/dist/{chunk-WTIYILY3.js → chunk-FGKBXUUA.js} +1 -1
  20. package/dist/{chunk-CI5OZ2QT.js → chunk-FUNCQCKN.js} +2 -2
  21. package/dist/{chunk-XG22XKKD.js → chunk-GWT5UF5B.js} +2 -2
  22. package/dist/{chunk-K6RE23XX.js → chunk-H5QRRPBJ.js} +3 -3
  23. package/dist/{chunk-AZSLSAJB.js → chunk-JCG4575S.js} +2 -2
  24. package/dist/{chunk-YZTTFEL4.js → chunk-JMNUFA5M.js} +1 -1
  25. package/dist/{chunk-KZ6U6FKB.js → chunk-L6ODTHTX.js} +1 -1
  26. package/dist/{chunk-W4VA76QO.js → chunk-LKA43GYA.js} +1 -1
  27. package/dist/{chunk-6TI4WF5Y.js → chunk-LKL3SCBJ.js} +2 -2
  28. package/dist/{chunk-4OABUPIU.js → chunk-LN4KPZIX.js} +3 -3
  29. package/dist/chunk-MUXGTVEA.js +421 -0
  30. package/dist/{chunk-7C54CKFZ.js → chunk-MWBCQHKQ.js} +3 -3
  31. package/dist/{chunk-7JP52QQO.js → chunk-NI3LMETK.js} +2 -2
  32. package/dist/{chunk-HXLQSO66.js → chunk-NJTIJAFU.js} +2 -2
  33. package/dist/{chunk-YN7X7BEO.js → chunk-NQDHGK7C.js} +3 -3
  34. package/dist/{chunk-BLFJMMBW.js → chunk-OWM7E55U.js} +25 -2
  35. package/dist/{chunk-VPOOIEKT.js → chunk-P62INTQ5.js} +1 -1
  36. package/dist/{chunk-QVS6K4FP.js → chunk-Q56LZJFX.js} +1 -1
  37. package/dist/{chunk-BITS7UJY.js → chunk-R2ORCOTG.js} +2 -2
  38. package/dist/{chunk-2QXA52DG.js → chunk-RA74PUMP.js} +3 -3
  39. package/dist/{chunk-GSM664TX.js → chunk-TAVKGYH5.js} +24 -1
  40. package/dist/{chunk-OYDJHQNZ.js → chunk-UBVYPV7H.js} +2 -2
  41. package/dist/chunk-VR4DQTWR.js +391 -0
  42. package/dist/{chunk-M6H3SHIS.js → chunk-WPAZBAUW.js} +1 -1
  43. package/dist/{chunk-NWYYNE3Q.js → chunk-WTQH2NI2.js} +1 -1
  44. package/dist/{chunk-HRHKQOOF.js → chunk-X7RSKFWF.js} +1 -1
  45. package/dist/{chunk-CSZCWBOE.js → chunk-XDR6HC4U.js} +14 -7
  46. package/dist/{chunk-RU5U2YMT.js → chunk-Z4JZUIKE.js} +3 -3
  47. package/dist/{chunk-V7BWMOHO.js → chunk-Z4LFZWHH.js} +2 -2
  48. package/dist/{chunk-YNEEJCSZ.js → chunk-ZBX2CAOO.js} +2 -2
  49. package/dist/index.d.ts +57 -7
  50. package/dist/index.js +93 -37
  51. package/dist/meta.d.ts +1 -1
  52. package/dist/meta.js +129 -4
  53. package/dist/rich/ExcelUploadCard.js +4 -4
  54. package/dist/rich.d.ts +4 -2
  55. package/dist/rich.js +36 -8
  56. package/dist/styles.css +27 -0
  57. package/dist/{types-CYfIthux.d.ts → types-CMikpoN5.d.ts} +8 -1
  58. package/dist/widgets/ApprovalLineEditor.js +9 -8
  59. package/dist/widgets/ApprovalOverridesEditor.js +11 -10
  60. package/dist/widgets/ApprovalStepper.js +2 -2
  61. package/dist/widgets/AutocompleteField.js +9 -8
  62. package/dist/widgets/ColumnBoard.js +2 -2
  63. package/dist/widgets/DiffView.js +2 -2
  64. package/dist/widgets/Disclosure.js +2 -2
  65. package/dist/widgets/EventCalendar.d.ts +14 -10
  66. package/dist/widgets/EventCalendar.js +5 -3
  67. package/dist/widgets/FileDropzone.js +7 -7
  68. package/dist/widgets/ImageLightbox.js +5 -5
  69. package/dist/widgets/InlineEdit.js +2 -2
  70. package/dist/widgets/JsonField.js +2 -2
  71. package/dist/widgets/OptionSchemaEditor.js +3 -3
  72. package/dist/widgets/OptionValueFields.js +3 -3
  73. package/dist/widgets/OrgChart.js +3 -3
  74. package/dist/widgets/OrgChartBoard.d.ts +57 -4
  75. package/dist/widgets/OrgChartBoard.js +8 -8
  76. package/dist/widgets/PeriodFilter.js +6 -5
  77. package/dist/widgets/PermissionMatrixEditor.js +4 -4
  78. package/dist/widgets/PositionSelect.js +4 -4
  79. package/dist/widgets/PrintSheet.js +2 -2
  80. package/dist/widgets/ProfileEditDiffCard.js +4 -4
  81. package/dist/widgets/QRDisplay.js +4 -4
  82. package/dist/widgets/RankBoard.js +6 -6
  83. package/dist/widgets/RecordSheet.d.ts +48 -1
  84. package/dist/widgets/RecordSheet.js +2 -1
  85. package/dist/widgets/RoleMemberPicker.js +5 -5
  86. package/dist/widgets/SearchableMultiPicker.js +7 -6
  87. package/dist/widgets/SecureScreen.js +2 -2
  88. package/dist/widgets/StagePeriodEditor.js +6 -5
  89. package/dist/widgets/TagInput.js +2 -2
  90. package/dist/widgets/TagMultiSelect.js +4 -4
  91. package/dist/widgets/Timeline.js +3 -3
  92. package/dist/widgets/TreePicker.js +8 -7
  93. package/dist/widgets/TreeView.d.ts +16 -2
  94. package/dist/widgets/TreeView.js +7 -6
  95. package/dist/widgets/index.d.ts +1 -0
  96. package/dist/widgets/index.js +76 -75
  97. package/package.json +1 -1
  98. package/dist/chunk-IB2ZPUTF.js +0 -172
@@ -1,8 +1,9 @@
1
1
  import { parseJobPositionRow } from './chunk-XRHR452N.js';
2
- import { usePanZoomCanvas, MIN_ZOOM } from './chunk-CSZCWBOE.js';
3
- import { ImageLightbox } from './chunk-KZ6U6FKB.js';
4
- import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-YNEEJCSZ.js';
5
- import { warnOnce, Badge, Button, IconButton, Icon } from './chunk-BLFJMMBW.js';
2
+ import { MIN_ZOOM, usePanZoomCanvas } from './chunk-XDR6HC4U.js';
3
+ import { ImageLightbox } from './chunk-L6ODTHTX.js';
4
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-ZBX2CAOO.js';
5
+ import { useIsTabletDown } from './chunk-TAVKGYH5.js';
6
+ import { warnOnce, Badge, Button, IconButton, Icon } from './chunk-OWM7E55U.js';
6
7
  import { boxed } from './chunk-3JTBOABA.js';
7
8
  import { useState, useMemo, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
8
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -43,6 +44,13 @@ var TOP_GAP = 58;
43
44
  var ROOT_ROW_GAP = 70;
44
45
  var DEFAULT_VIEW = { scale: 0.82, x: 48, y: 48 };
45
46
  var FIT_PAD = 16;
47
+ var FIT_MIN_ZOOM = 0.1;
48
+ var FLOW_PAD = 16;
49
+ var FLOW_ITEM_WIDTH = 264;
50
+ var FLOW_GAP = 12;
51
+ var FLOW_ROW_GAP = 12;
52
+ var FLOW_BLOCK_GAP = 24;
53
+ var FLOW_TOP = 72;
46
54
  function fitCanvasView(viewportWidth, viewportHeight, layoutWidth, layoutHeight) {
47
55
  if (!(viewportWidth > 0) || !(viewportHeight > 0) || !(layoutWidth > 0) || !(layoutHeight > 0)) {
48
56
  return DEFAULT_VIEW;
@@ -52,7 +60,7 @@ function fitCanvasView(viewportWidth, viewportHeight, layoutWidth, layoutHeight)
52
60
  (viewportHeight - FIT_PAD * 2) / layoutHeight,
53
61
  1
54
62
  );
55
- const scale = Math.max(MIN_ZOOM, raw);
63
+ const scale = Math.max(FIT_MIN_ZOOM, raw);
56
64
  const width = layoutWidth * scale;
57
65
  const height = layoutHeight * scale;
58
66
  return {
@@ -61,6 +69,11 @@ function fitCanvasView(viewportWidth, viewportHeight, layoutWidth, layoutHeight)
61
69
  y: height <= viewportHeight - FIT_PAD * 2 ? (viewportHeight - height) / 2 : FIT_PAD
62
70
  };
63
71
  }
72
+ function fitFlowView(viewportWidth, layoutWidth) {
73
+ if (!(viewportWidth > 0) || !(layoutWidth > 0)) return { scale: 1, x: 0, y: FLOW_TOP };
74
+ const scale = Math.max(FIT_MIN_ZOOM, Math.min(1, (viewportWidth - FIT_PAD * 2) / layoutWidth));
75
+ return { scale, x: (viewportWidth - layoutWidth * scale) / 2, y: FLOW_TOP };
76
+ }
64
77
  var DUTY_ORDER = [
65
78
  "\uD68C\uC7A5",
66
79
  "\uBD80\uD68C\uC7A5",
@@ -135,6 +148,9 @@ var OCB_CSS = `
135
148
  @media screen and (max-width: 767.98px) {
136
149
  .erp-ocb-viewport { min-height: 320px; height: min(var(--erp-ocb-h, 72vh), 68dvh); }
137
150
  }
151
+ /* \uD750\uB984 \uBC30\uCE58(\uD0DC\uBE14\uB9BF \uC774\uD558, \uC804\uCCB4\uD654\uBA74 \uBC16): \uB192\uC774\uB294 \uB0B4\uC6A9\uC744 \uB530\uB77C\uAC04\uB2E4(\uC778\uB77C\uC778 height) \u2014 \uC138\uB85C\uB294 \uD398\uC774\uC9C0\uAC00 \uC2A4\uD06C\uB864\uD55C\uB2E4. */
152
+ .erp-ocb-viewport.erp-ocb-viewport--flow { min-height: 0; }
153
+ .erp-ocb-toggle { flex-shrink: 0; margin-left: auto; opacity: .9; }
138
154
  `;
139
155
  async function fetchAllPages(api, endpoint, pick) {
140
156
  const all = [];
@@ -353,13 +369,20 @@ function leadershipRankOf(employee, leadership) {
353
369
  function leadershipEmployees(employees, leadership = LEADERSHIP_MATCH) {
354
370
  return employees.map((employee) => ({ employee, rank: leadershipRankOf(employee, leadership) })).filter((entry) => entry.rank >= 0).sort((a, b) => a.rank - b.rank).slice(0, 3).map((entry) => entry.employee);
355
371
  }
356
- function buildCanvasLayout(model, employees, leadershipGroups) {
357
- const leadership = leadershipEmployees(employees, leadershipGroups);
358
- const leadershipIds = new Set(leadership.map((employee) => employee.id));
372
+ function boardRootsOf(model) {
359
373
  const topGroup = model.visibleRoots.length === 1 ? model.visibleRoots[0] : null;
360
374
  const topChildren = topGroup ? model.childrenByParent.get(topGroup.id) ?? [] : [];
361
375
  const hasCompanyRoot = !!topGroup && topChildren.length > 1;
362
- const boardRoots = hasCompanyRoot ? topChildren : model.visibleRoots;
376
+ return { topGroup, hasCompanyRoot, boardRoots: hasCompanyRoot ? topChildren : model.visibleRoots };
377
+ }
378
+ function flowToggleIds(model) {
379
+ const { boardRoots } = boardRootsOf(model);
380
+ return boardRoots.length > 1 ? boardRoots.map((group) => group.id) : [];
381
+ }
382
+ function buildCanvasLayout(model, employees, leadershipGroups) {
383
+ const leadership = leadershipEmployees(employees, leadershipGroups);
384
+ const leadershipIds = new Set(leadership.map((employee) => employee.id));
385
+ const { topGroup, hasCompanyRoot, boardRoots } = boardRootsOf(model);
363
386
  const rootBlocks = boardRoots.map(
364
387
  (group, index) => buildRootBlock(group, model, leadershipIds, index)
365
388
  );
@@ -444,6 +467,118 @@ function buildCanvasLayout(model, employees, leadershipGroups) {
444
467
  connectors
445
468
  };
446
469
  }
470
+ function buildFlowLayout(model, employees, leadershipGroups, expanded, availableWidth) {
471
+ const leadership = leadershipEmployees(employees, leadershipGroups);
472
+ const leadershipIds = new Set(leadership.map((employee) => employee.id));
473
+ const { topGroup, hasCompanyRoot, boardRoots } = boardRootsOf(model);
474
+ const single = boardRoots.length === 1;
475
+ const blocks = boardRoots.map(
476
+ (group, index) => single || expanded.has(group.id) ? buildRootBlock(group, model, leadershipIds, index) : null
477
+ );
478
+ const inner = Math.max(FLOW_ITEM_WIDTH, availableWidth - FLOW_PAD * 2);
479
+ const columns = Math.max(
480
+ 1,
481
+ Math.min(boardRoots.length, Math.floor((inner + FLOW_GAP) / (FLOW_ITEM_WIDTH + FLOW_GAP)))
482
+ );
483
+ const gridWidth = columns * FLOW_ITEM_WIDTH + (columns - 1) * FLOW_GAP;
484
+ const contentWidth = Math.max(
485
+ gridWidth,
486
+ leadership.length > 0 ? LEADER_WIDTH : 0,
487
+ ...blocks.map((block) => block?.width ?? 0)
488
+ );
489
+ const centerX = FLOW_PAD + contentWidth / 2;
490
+ const groups = [];
491
+ const placedEmployees = [];
492
+ const connectors = [];
493
+ let y = FLOW_PAD;
494
+ let bottom = FLOW_PAD;
495
+ if (leadership.length > 0) {
496
+ leadership.forEach((employee, index) => {
497
+ placedEmployees.push({
498
+ employee,
499
+ x: centerX - LEADER_WIDTH / 2,
500
+ y: y + index * (LEADER_HEIGHT + EMPLOYEE_GAP),
501
+ featured: true
502
+ });
503
+ });
504
+ y += leadership.length * LEADER_HEIGHT + (leadership.length - 1) * EMPLOYEE_GAP;
505
+ bottom = y;
506
+ }
507
+ if (hasCompanyRoot && topGroup) {
508
+ if (leadership.length > 0) {
509
+ connectors.push({ fromX: centerX, fromY: y, toX: centerX, toY: y + FLOW_BLOCK_GAP });
510
+ y += FLOW_BLOCK_GAP;
511
+ }
512
+ groups.push({
513
+ group: topGroup,
514
+ x: centerX - GROUP_WIDTH / 2,
515
+ y,
516
+ width: GROUP_WIDTH,
517
+ color: BOARD_COLORS[0],
518
+ depth: 0,
519
+ totalMembers: employeesInGroup(topGroup.id, model).length
520
+ });
521
+ y += GROUP_HEIGHT;
522
+ bottom = y;
523
+ }
524
+ if (bottom > FLOW_PAD) y += FLOW_BLOCK_GAP;
525
+ const gridX = FLOW_PAD + (contentWidth - gridWidth) / 2;
526
+ let column = 0;
527
+ const closeRow = (gap) => {
528
+ if (column === 0) return;
529
+ y += GROUP_HEIGHT + gap;
530
+ column = 0;
531
+ };
532
+ boardRoots.forEach((group, index) => {
533
+ const block = blocks[index];
534
+ if (block) {
535
+ closeRow(FLOW_BLOCK_GAP);
536
+ const bx = FLOW_PAD + (contentWidth - block.width) / 2;
537
+ const by = y;
538
+ groups.push(
539
+ ...block.groups.map((item, itemIndex) => ({
540
+ ...item,
541
+ x: item.x + bx,
542
+ y: item.y + by,
543
+ // 블록의 첫 그룹이 본부 머리다(buildRootBlock) — 거기에 접기 손잡이를 단다.
544
+ ...itemIndex === 0 && !single ? { toggle: "expanded" } : {}
545
+ }))
546
+ );
547
+ placedEmployees.push(...block.employees.map((item) => ({ ...item, x: item.x + bx, y: item.y + by })));
548
+ connectors.push(
549
+ ...block.connectors.map((item) => ({
550
+ fromX: item.fromX + bx,
551
+ fromY: item.fromY + by,
552
+ toX: item.toX + bx,
553
+ toY: item.toY + by
554
+ }))
555
+ );
556
+ bottom = by + block.height;
557
+ y = bottom + FLOW_BLOCK_GAP;
558
+ return;
559
+ }
560
+ groups.push({
561
+ group,
562
+ x: gridX + column * (FLOW_ITEM_WIDTH + FLOW_GAP),
563
+ y,
564
+ width: FLOW_ITEM_WIDTH,
565
+ color: BOARD_COLORS[index % BOARD_COLORS.length],
566
+ depth: 0,
567
+ totalMembers: employeesInGroup(group.id, model).length,
568
+ toggle: "collapsed"
569
+ });
570
+ bottom = y + GROUP_HEIGHT;
571
+ column += 1;
572
+ if (column === columns) closeRow(FLOW_ROW_GAP);
573
+ });
574
+ return {
575
+ width: contentWidth + FLOW_PAD * 2,
576
+ height: bottom + FLOW_PAD,
577
+ groups,
578
+ employees: placedEmployees,
579
+ connectors
580
+ };
581
+ }
447
582
  function buildRootBlock(group, model, leadershipIds, siblingIndex) {
448
583
  const teams = model.childrenByParent.get(group.id) ?? [];
449
584
  const directEmployees = groupEmployees(group.id, model, leadershipIds);
@@ -755,9 +890,11 @@ var SELECTED_RING = "0 0 0 2px var(--wp-surface-card), 0 0 0 4px var(--wp-accent
755
890
  function CanvasGroupNode({
756
891
  item,
757
892
  selected,
758
- onSelect
893
+ onSelect,
894
+ onToggle
759
895
  }) {
760
896
  const height = item.height ?? GROUP_HEIGHT;
897
+ const toggle = item.toggle && onToggle ? item.toggle : void 0;
761
898
  if (item.vertical) {
762
899
  return /* @__PURE__ */ jsxs(
763
900
  "button",
@@ -832,7 +969,11 @@ function CanvasGroupNode({
832
969
  boxShadow: selected ? SELECTED_RING : "var(--wp-shadow-xs)",
833
970
  cursor: "pointer"
834
971
  },
835
- onClick: () => onSelect(item.group.id),
972
+ "aria-expanded": toggle ? toggle === "expanded" : void 0,
973
+ onClick: () => {
974
+ onSelect(item.group.id);
975
+ if (toggle) onToggle?.(item.group.id);
976
+ },
836
977
  children: [
837
978
  /* @__PURE__ */ jsx(Icon, { name: "building-2", size: 16, style: { flexShrink: 0 } }),
838
979
  /* @__PURE__ */ jsxs(
@@ -854,7 +995,8 @@ function CanvasGroupNode({
854
995
  ")"
855
996
  ]
856
997
  }
857
- )
998
+ ),
999
+ toggle ? /* @__PURE__ */ jsx(Icon, { className: "erp-ocb-toggle", name: toggle === "expanded" ? "chevron-up" : "chevron-down", size: 16 }) : null
858
1000
  ]
859
1001
  }
860
1002
  );
@@ -936,6 +1078,10 @@ function OrganizationCanvas({
936
1078
  onOpenCard,
937
1079
  height
938
1080
  }) {
1081
+ const narrow = useIsTabletDown();
1082
+ const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
1083
+ const [boxWidth, setBoxWidth] = useState(0);
1084
+ const [minScale, setMinScale] = useState(MIN_ZOOM);
939
1085
  const {
940
1086
  viewportRef,
941
1087
  view,
@@ -948,33 +1094,58 @@ function OrganizationCanvas({
948
1094
  zoomAt,
949
1095
  toggleFullscreen,
950
1096
  pointerHandlers
951
- } = usePanZoomCanvas({ stopSelector: "[data-org-card]", initialView: DEFAULT_VIEW });
1097
+ } = usePanZoomCanvas({
1098
+ stopSelector: "[data-org-card]",
1099
+ initialView: DEFAULT_VIEW,
1100
+ minScale,
1101
+ // 흐름 배치(전체화면 밖)는 캔버스가 페이지 세로를 덮는다 — 그냥 휠은 페이지 스크롤, Ctrl+휠·트랙패드 핀치만 줌.
1102
+ wheelNeedsCtrl: narrow
1103
+ });
1104
+ const flowAuto = narrow && !isFullscreen;
952
1105
  const layout = useMemo(
953
- () => buildCanvasLayout(model, employees, leadership),
954
- [model, employees, leadership]
1106
+ () => narrow ? buildFlowLayout(model, employees, leadership, expanded, Math.max(0, boxWidth - FIT_PAD * 2)) : buildCanvasLayout(model, employees, leadership),
1107
+ [narrow, model, employees, leadership, expanded, boxWidth]
955
1108
  );
1109
+ const toggleIds = useMemo(() => narrow ? flowToggleIds(model) : [], [narrow, model]);
1110
+ const allExpanded = toggleIds.length > 0 && toggleIds.every((id) => expanded.has(id));
1111
+ const toggleGroup = useCallback((groupId) => {
1112
+ setExpanded((current) => {
1113
+ const next = new Set(current);
1114
+ if (next.has(groupId)) next.delete(groupId);
1115
+ else next.add(groupId);
1116
+ return next;
1117
+ });
1118
+ }, []);
956
1119
  const fitView = useCallback(() => {
957
1120
  const el = viewportRef.current;
958
1121
  if (!el) return;
959
1122
  interactedRef.current = false;
960
- setView(fitCanvasView(el.clientWidth, el.clientHeight, layout.width, layout.height));
961
- }, [layout.width, layout.height, setView, viewportRef, interactedRef]);
1123
+ const next = flowAuto ? fitFlowView(el.clientWidth, layout.width) : fitCanvasView(el.clientWidth, el.clientHeight, layout.width, layout.height);
1124
+ setMinScale(Math.min(MIN_ZOOM, next.scale));
1125
+ setView(next);
1126
+ }, [flowAuto, layout.width, layout.height, setView, viewportRef, interactedRef]);
1127
+ useLayoutEffect(() => {
1128
+ const el = viewportRef.current;
1129
+ if (el && el.clientWidth !== boxWidth) setBoxWidth(el.clientWidth);
1130
+ });
962
1131
  useLayoutEffect(() => {
963
1132
  fitView();
964
1133
  }, [fitView]);
965
1134
  useEffect(() => {
966
1135
  const el = viewportRef.current;
967
1136
  if (!el || typeof ResizeObserver === "undefined") return;
968
- let last = `${el.clientWidth}x${el.clientHeight}`;
1137
+ const sizeOf = () => flowAuto ? `${el.clientWidth}` : `${el.clientWidth}x${el.clientHeight}`;
1138
+ let last = sizeOf();
969
1139
  const observer = new ResizeObserver(() => {
970
- const size = `${el.clientWidth}x${el.clientHeight}`;
1140
+ const size = sizeOf();
971
1141
  if (size === last) return;
972
1142
  last = size;
1143
+ setBoxWidth(el.clientWidth);
973
1144
  if (!interactedRef.current) fitView();
974
1145
  });
975
1146
  observer.observe(el);
976
1147
  return () => observer.disconnect();
977
- }, [fitView, viewportRef, interactedRef]);
1148
+ }, [fitView, flowAuto, viewportRef, interactedRef]);
978
1149
  const savedScrollRef = useRef([]);
979
1150
  const rememberScroll = () => {
980
1151
  const saved = [];
@@ -1013,10 +1184,13 @@ function OrganizationCanvas({
1013
1184
  const viewportClass = [
1014
1185
  "erp-ocb-viewport",
1015
1186
  dragging ? "erp-ocb-viewport--dragging" : "",
1016
- pseudoFullscreen ? "erp-ocb-viewport--pseudo-fs" : ""
1187
+ pseudoFullscreen ? "erp-ocb-viewport--pseudo-fs" : "",
1188
+ flowAuto ? "erp-ocb-viewport--flow" : ""
1017
1189
  ].filter(Boolean).join(" ");
1190
+ const stageY = flowAuto ? FLOW_TOP : view.y;
1018
1191
  const viewportStyle = {
1019
- "--erp-ocb-h": typeof height === "number" ? `${height}px` : height
1192
+ "--erp-ocb-h": typeof height === "number" ? `${height}px` : height,
1193
+ ...flowAuto ? { height: Math.ceil(FLOW_TOP + layout.height * view.scale + FIT_PAD) } : null
1020
1194
  };
1021
1195
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1022
1196
  pseudoFullscreen ? /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "erp-ocb-placeholder", style: { height: flowHeightRef.current } }) : null,
@@ -1054,6 +1228,15 @@ function OrganizationCanvas({
1054
1228
  ] }),
1055
1229
  /* @__PURE__ */ jsx(IconButton, { icon: "plus", label: "\uD655\uB300", size: "sm", onClick: () => zoomStep(0.1) }),
1056
1230
  /* @__PURE__ */ jsx(IconButton, { icon: "locate-fixed", label: "\uD654\uBA74\uC5D0 \uB9DE\uCDA4", size: "sm", onClick: fitView }),
1231
+ toggleIds.length > 0 ? /* @__PURE__ */ jsx(
1232
+ IconButton,
1233
+ {
1234
+ icon: allExpanded ? "folder" : "folder-open",
1235
+ label: allExpanded ? "\uBCF8\uBD80 \uBAA8\uB450 \uC811\uAE30" : "\uBCF8\uBD80 \uBAA8\uB450 \uD3BC\uCE58\uAE30",
1236
+ size: "sm",
1237
+ onClick: () => setExpanded(allExpanded ? /* @__PURE__ */ new Set() : new Set(toggleIds))
1238
+ }
1239
+ ) : null,
1057
1240
  /* @__PURE__ */ jsx(
1058
1241
  IconButton,
1059
1242
  {
@@ -1081,7 +1264,7 @@ function OrganizationCanvas({
1081
1264
  top: 0,
1082
1265
  width: layout.width,
1083
1266
  height: layout.height,
1084
- transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`,
1267
+ transform: `translate(${view.x}px, ${stageY}px) scale(${view.scale})`,
1085
1268
  transformOrigin: "0 0"
1086
1269
  },
1087
1270
  children: [
@@ -1108,7 +1291,8 @@ function OrganizationCanvas({
1108
1291
  {
1109
1292
  item,
1110
1293
  selected: selectedGroupId === item.group.id,
1111
- onSelect
1294
+ onSelect,
1295
+ onToggle: toggleGroup
1112
1296
  },
1113
1297
  item.group.id
1114
1298
  )),
@@ -1328,4 +1512,4 @@ function OrgChartBoardImpl({
1328
1512
  }
1329
1513
  var OrgChartBoard = boxed(OrgChartBoardImpl, "OrgChartBoard");
1330
1514
 
1331
- export { OrgChartBoard, buildModel, compareMembers, fitCanvasView, isUnassignedGroup, leadershipEmployees, orgVocabOf, pickEmployees, pickGroups, pickPositions };
1515
+ export { OrgChartBoard, buildFlowLayout, buildModel, compareMembers, fitCanvasView, fitFlowView, flowToggleIds, isUnassignedGroup, leadershipEmployees, orgVocabOf, pickEmployees, pickGroups, pickPositions };
@@ -1,7 +1,7 @@
1
- import { orderCircleStyle, ellipsisStyle, unwrapRows, uid } from './chunk-LKQJJSVJ.js';
2
- import { includesLooseSearch } from './chunk-4OABUPIU.js';
3
- import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-YNEEJCSZ.js';
4
- import { Icon, Badge, IconButton, Field, Select, Input, Tag, Spinner, Button, isPlainObject } from './chunk-BLFJMMBW.js';
1
+ import { orderCircleStyle, ellipsisStyle, unwrapRows, uid } from './chunk-63GM4S25.js';
2
+ import { includesLooseSearch } from './chunk-LN4KPZIX.js';
3
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-ZBX2CAOO.js';
4
+ import { Icon, Badge, IconButton, Field, Select, Input, Tag, Spinner, Button, isPlainObject } from './chunk-OWM7E55U.js';
5
5
  import { boxed } from './chunk-3JTBOABA.js';
6
6
  import React, { useState, useId, useRef, useEffect } from 'react';
7
7
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,8 +1,8 @@
1
- import { TagMultiSelect } from './chunk-6TI4WF5Y.js';
2
- import { ApprovalLineEditor, stepTypeIcon, stepLabel } from './chunk-IQXLNICY.js';
3
- import { orderCircleStyle, unwrapRows, uid } from './chunk-LKQJJSVJ.js';
4
- import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-YNEEJCSZ.js';
5
- import { Card, Icon, Input, IconButton, Field, Button, Badge } from './chunk-BLFJMMBW.js';
1
+ import { TagMultiSelect } from './chunk-LKL3SCBJ.js';
2
+ import { ApprovalLineEditor, stepTypeIcon, stepLabel } from './chunk-DCSUBFFG.js';
3
+ import { orderCircleStyle, unwrapRows, uid } from './chunk-63GM4S25.js';
4
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-ZBX2CAOO.js';
5
+ import { Card, Icon, Input, IconButton, Field, Button, Badge } from './chunk-OWM7E55U.js';
6
6
  import { boxed } from './chunk-3JTBOABA.js';
7
7
  import React, { useRef, useState, useEffect } from 'react';
8
8
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { Textarea, Icon } from './chunk-BLFJMMBW.js';
1
+ import { Textarea, Icon } from './chunk-OWM7E55U.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import { useState, useRef, useId, useEffect, useCallback } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { Icon, Badge, Tooltip } from './chunk-BLFJMMBW.js';
1
+ import { Icon, Badge, Tooltip } from './chunk-OWM7E55U.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import React from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
- import { OverlayLayer } from './chunk-BITS7UJY.js';
2
- import { Dialog, Button, warnOnce } from './chunk-BLFJMMBW.js';
1
+ import { OverlayLayer } from './chunk-R2ORCOTG.js';
2
+ import { Dialog, Button, warnOnce } from './chunk-OWM7E55U.js';
3
3
  import { createContext, useContext, useMemo, useRef, useCallback, useState } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -1,5 +1,5 @@
1
- import { DatePicker, TimePicker } from './chunk-X2NIRVLN.js';
2
- import { Field } from './chunk-BLFJMMBW.js';
1
+ import { DatePicker, TimePicker } from './chunk-4VQIKSDS.js';
2
+ import { Field } from './chunk-OWM7E55U.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import { useState, useRef, useEffect, useMemo } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,6 +1,6 @@
1
- import { resolveTreeNodes, TreeView } from './chunk-2KQL6DYG.js';
2
- import { OverlayLayer } from './chunk-BITS7UJY.js';
3
- import { Field, Dialog, Icon, Button, Input } from './chunk-BLFJMMBW.js';
1
+ import { resolveTreeNodes, TreeView } from './chunk-AP33B24H.js';
2
+ import { OverlayLayer } from './chunk-R2ORCOTG.js';
3
+ import { Field, Dialog, Icon, Button, Input } from './chunk-OWM7E55U.js';
4
4
  import { boxed } from './chunk-3JTBOABA.js';
5
5
  import { useState, useMemo } from 'react';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
- import { useOverlayLayer, OverlayContainerContext, getOverlayRoot } from './chunk-BITS7UJY.js';
2
- import { IconButton } from './chunk-BLFJMMBW.js';
1
+ import { useOverlayLayer, OverlayContainerContext, getOverlayRoot } from './chunk-R2ORCOTG.js';
2
+ import { IconButton } from './chunk-OWM7E55U.js';
3
3
  import { useState, useRef, useCallback, useEffect, useContext } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { IconButton, Input } from './chunk-BLFJMMBW.js';
1
+ import { IconButton, Input } from './chunk-OWM7E55U.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import { useState, useRef, useEffect, useCallback } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { ImageViewer } from './chunk-AZSLSAJB.js';
1
+ import { ImageViewer } from './chunk-JCG4575S.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import { useState } from 'react';
4
4
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
1
  import { HoistedStyle } from './chunk-RIZRM7OO.js';
2
- import { Tag } from './chunk-BLFJMMBW.js';
2
+ import { Tag } from './chunk-OWM7E55U.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import { useState, useMemo, useRef, useEffect } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
- import { useSduiAdapter, hostFetchOf } from './chunk-YNEEJCSZ.js';
2
- import { Badge, Tag } from './chunk-BLFJMMBW.js';
1
+ import { useSduiAdapter, hostFetchOf } from './chunk-ZBX2CAOO.js';
2
+ import { Badge, Tag } from './chunk-OWM7E55U.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import { useRef, useEffect, useMemo, useState } from 'react';
5
5
  import { jsx } from 'react/jsx-runtime';
@@ -1,7 +1,7 @@
1
- import { DateRangePicker } from './chunk-X2NIRVLN.js';
2
- import { useOverlayLayer } from './chunk-BITS7UJY.js';
1
+ import { DateRangePicker } from './chunk-4VQIKSDS.js';
2
+ import { useOverlayLayer } from './chunk-R2ORCOTG.js';
3
3
  import { HoistedStyle } from './chunk-RIZRM7OO.js';
4
- import { Input, Button, IconButton, Tag, Field, Checkbox } from './chunk-BLFJMMBW.js';
4
+ import { Input, Button, IconButton, Tag, Field, Checkbox } from './chunk-OWM7E55U.js';
5
5
  import { useRef, useEffect, useState, useMemo } from 'react';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7