@wipco/sdui 1.6.1 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DatePicker-DX9juElE.d.ts → DatePicker-BOOKdd5x.d.ts} +6 -6
- package/dist/calendar-nav-BSGsgJc4.d.ts +54 -0
- package/dist/{chunk-OKNF2SBB.js → chunk-2FHNGMBT.js} +3 -3
- package/dist/{chunk-AJ3KDBEE.js → chunk-466W3XNK.js} +1 -1
- package/dist/{chunk-UC725BGW.js → chunk-5FZBH3T5.js} +1 -1
- package/dist/{chunk-XUVWIRZJ.js → chunk-5LIFU57A.js} +2 -2
- package/dist/{chunk-VAXRTBJH.js → chunk-63GM4S25.js} +1 -1
- package/dist/{chunk-KO3EUNWB.js → chunk-7YTZB5LQ.js} +3 -3
- package/dist/{chunk-SGGIWPP6.js → chunk-7ZCM6XEM.js} +2 -2
- package/dist/{chunk-IIRVHSNK.js → chunk-A3SO6H47.js} +2 -2
- package/dist/{chunk-VSNHDHGS.js → chunk-B5MB64YQ.js} +209 -25
- package/dist/{chunk-US3K6Q5T.js → chunk-ENZBX4AR.js} +1 -1
- package/dist/{chunk-CUFGH3LB.js → chunk-FGKBXUUA.js} +1 -1
- package/dist/{chunk-VR5MFUD2.js → chunk-FUNCQCKN.js} +2 -2
- package/dist/{chunk-O4EMQWCV.js → chunk-FYVDPEJU.js} +120 -26
- package/dist/{chunk-GKYUDTMI.js → chunk-J6YDTBKY.js} +2 -2
- package/dist/{chunk-UZZLMPRW.js → chunk-JCG4575S.js} +2 -2
- package/dist/{chunk-34BABTH4.js → chunk-JMNUFA5M.js} +1 -1
- package/dist/{chunk-FP32LNFK.js → chunk-KAQ2VZBM.js} +166 -193
- package/dist/{chunk-A57GKV5F.js → chunk-KXTQOP3E.js} +3 -3
- package/dist/{chunk-3PT5OBOU.js → chunk-L6ODTHTX.js} +1 -1
- package/dist/{chunk-OQ7A3SFM.js → chunk-LKA43GYA.js} +1 -1
- package/dist/{chunk-GSXD52XG.js → chunk-LKL3SCBJ.js} +2 -2
- package/dist/{chunk-J7BZF4T4.js → chunk-MANONABB.js} +4 -4
- package/dist/{chunk-XFKNU6QN.js → chunk-NI3LMETK.js} +2 -2
- package/dist/{chunk-ESAFQXEU.js → chunk-NJTIJAFU.js} +2 -2
- package/dist/{chunk-PCFH74RD.js → chunk-NQDHGK7C.js} +3 -3
- package/dist/{chunk-YYAGE733.js → chunk-OWM7E55U.js} +8 -0
- package/dist/{chunk-OCYJ2ZOV.js → chunk-P62INTQ5.js} +1 -1
- package/dist/{chunk-7G6J6QFC.js → chunk-Q56LZJFX.js} +1 -1
- package/dist/{chunk-KDKRFWIF.js → chunk-QAGJ6FRE.js} +5 -5
- package/dist/chunk-QVNJMZUF.js +462 -0
- package/dist/{chunk-BITS7UJY.js → chunk-R2ORCOTG.js} +2 -2
- package/dist/{chunk-VQKX5PJ4.js → chunk-RA74PUMP.js} +3 -3
- package/dist/{chunk-USNPJEXH.js → chunk-RJYPRWWS.js} +4 -4
- package/dist/{chunk-TB7G5DXQ.js → chunk-S6GJRP5T.js} +2 -2
- package/dist/{chunk-TWUKNUUQ.js → chunk-SGR2QDEG.js} +2 -2
- package/dist/{chunk-3PCR73OC.js → chunk-T5LLOSHE.js} +3 -3
- package/dist/{chunk-GSM664TX.js → chunk-TAVKGYH5.js} +24 -1
- package/dist/{chunk-BV37UCDF.js → chunk-UBVYPV7H.js} +2 -2
- package/dist/chunk-VR4DQTWR.js +391 -0
- package/dist/{chunk-6YBSBC77.js → chunk-WPAZBAUW.js} +1 -1
- package/dist/{chunk-4YPPL7JK.js → chunk-WTQH2NI2.js} +1 -1
- package/dist/{chunk-NLSR3JYU.js → chunk-X7RSKFWF.js} +1 -1
- package/dist/{chunk-CSZCWBOE.js → chunk-XDR6HC4U.js} +14 -7
- package/dist/{chunk-EUSC5NMZ.js → chunk-Z4JZUIKE.js} +3 -3
- package/dist/{chunk-EGFDELSF.js → chunk-Z4LFZWHH.js} +2 -2
- package/dist/{chunk-JHFA5RDN.js → chunk-ZBX2CAOO.js} +2 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +27 -19
- package/dist/meta.js +14 -1
- package/dist/rich/ExcelUploadCard.js +4 -4
- package/dist/rich.d.ts +2 -1
- package/dist/rich.js +7 -6
- package/dist/styles.css +10 -0
- package/dist/widgets/ApprovalLineEditor.js +9 -8
- package/dist/widgets/ApprovalOverridesEditor.js +11 -10
- package/dist/widgets/ApprovalStepper.js +2 -2
- package/dist/widgets/AutocompleteField.js +9 -8
- package/dist/widgets/ColumnBoard.js +2 -2
- package/dist/widgets/DiffView.js +2 -2
- package/dist/widgets/Disclosure.js +2 -2
- package/dist/widgets/EventCalendar.d.ts +14 -10
- package/dist/widgets/EventCalendar.js +5 -3
- package/dist/widgets/FileDropzone.js +7 -7
- package/dist/widgets/ImageLightbox.js +5 -5
- package/dist/widgets/InlineEdit.js +2 -2
- package/dist/widgets/JsonField.js +2 -2
- package/dist/widgets/OptionSchemaEditor.js +3 -3
- package/dist/widgets/OptionValueFields.js +3 -3
- package/dist/widgets/OrgChart.js +3 -3
- package/dist/widgets/OrgChartBoard.d.ts +57 -4
- package/dist/widgets/OrgChartBoard.js +8 -8
- package/dist/widgets/PeriodFilter.js +6 -5
- package/dist/widgets/PermissionMatrixEditor.js +4 -4
- package/dist/widgets/PositionSelect.js +4 -4
- package/dist/widgets/PrintSheet.js +2 -2
- package/dist/widgets/ProfileEditDiffCard.js +4 -4
- package/dist/widgets/QRDisplay.js +4 -4
- package/dist/widgets/RankBoard.js +6 -6
- package/dist/widgets/RecordSheet.d.ts +48 -1
- package/dist/widgets/RecordSheet.js +2 -1
- package/dist/widgets/RoleMemberPicker.js +5 -5
- package/dist/widgets/SearchableMultiPicker.js +7 -6
- package/dist/widgets/SecureScreen.js +2 -2
- package/dist/widgets/StagePeriodEditor.js +6 -5
- package/dist/widgets/TagInput.js +2 -2
- package/dist/widgets/TagMultiSelect.js +4 -4
- package/dist/widgets/Timeline.js +3 -3
- package/dist/widgets/TreePicker.js +8 -7
- package/dist/widgets/TreeView.js +7 -6
- package/dist/widgets/index.d.ts +1 -0
- package/dist/widgets/index.js +76 -75
- package/package.json +1 -1
- package/dist/chunk-IB2ZPUTF.js +0 -172
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { S as SduiBoxProps } from './layout-DmRikwZb.js';
|
|
2
2
|
import React__default from 'react';
|
|
3
|
+
import { C as CalendarNavLocale } from './calendar-nav-BSGsgJc4.js';
|
|
3
4
|
|
|
4
5
|
interface TimePickerProps {
|
|
5
6
|
/** 선택된 시각 (HH:MM, 24시간제). 주어지면 controlled로 동작. */
|
|
@@ -24,16 +25,15 @@ interface TimePickerProps {
|
|
|
24
25
|
/** 바깥에서 온 `style`·`className`을 뿌리에 얹어 내보낸다. 까닭은 {@link ./boxed.tsx}에 있다. */
|
|
25
26
|
declare const TimePicker: React__default.FC<TimePickerProps & SduiBoxProps>;
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
/**
|
|
29
|
+
* 머리글·월·연도 보기의 글자(formatCaption·prevMonthLabel·prevYearLabel …)는 공용 부품의 로캘
|
|
30
|
+
* {@link CalendarNavLocale} 에서 온다 — 여기는 날짜 격자와 패널에만 있는 자리를 더한다.
|
|
31
|
+
*/
|
|
32
|
+
interface DatePickerLocale extends CalendarNavLocale {
|
|
28
33
|
/** 요일 머리글 — **일요일부터** 7개. 주 시작이 달라도 이 순서로 주면 내부에서 돌린다. */
|
|
29
34
|
weekdays?: readonly string[];
|
|
30
35
|
/** 주 시작 요일 (0=일 … 6=토). */
|
|
31
36
|
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
32
|
-
/** 달력 위 연월 캡션 (month는 1–12). */
|
|
33
|
-
formatCaption?: (year: number, month: number) => string;
|
|
34
|
-
/** 이전/다음 달 버튼의 낭독 라벨 */
|
|
35
|
-
prevMonthLabel?: string;
|
|
36
|
-
nextMonthLabel?: string;
|
|
37
37
|
/** 달력 패널의 낭독 라벨 */
|
|
38
38
|
calendarLabel?: string;
|
|
39
39
|
/** 기간 달력에서 시작·종료 칸의 낭독 라벨 (DateRangePicker) */
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 달력 머리글 탐색과 월·연도 보기 — DatePicker(날짜·기간 팝업 달력)와 EventCalendar(일정 캘린더)가
|
|
3
|
+
* **같은 부품**을 쓴다.
|
|
4
|
+
*
|
|
5
|
+
* 머리글은 `<< < [제목] > >>` 한 줄이다 — 제목이 가운데, 왼쪽에 큰 걸음·작은 걸음 뒤로, 오른쪽에
|
|
6
|
+
* 작은 걸음·큰 걸음 앞으로. 예전에는 이전·다음 버튼이 오른쪽에 몰려 있어(기간 달력은 두 달 위에
|
|
7
|
+
* 한 쌍, 일정 캘린더는 '이전·오늘·다음'이 제목 왼쪽에) 어느 쪽으로 가는 버튼인지 한눈에 안 들어왔고,
|
|
8
|
+
* 연 단위 이동은 열두 번을 눌러야 했다(사용자 요청 2026-10-01).
|
|
9
|
+
*
|
|
10
|
+
* 제목을 누르면 한 단계 넓은 보기로 올라간다(윈도우 달력 방식): 일 → 월(그 해의 1~12월) →
|
|
11
|
+
* 연도(10년 + 앞뒤 회색 한 해씩). 고르면 한 단계 내려온다. 이 넓은 보기 둘이 `CalendarZoom` 이다 —
|
|
12
|
+
* 보는 달만 바꾸고 **선택 값은 건드리지 않는다**(값을 정하는 것은 언제나 일 보기의 날짜 칸이다).
|
|
13
|
+
*
|
|
14
|
+
* 걸음 폭은 보기 단위를 따른다: 월 보기(1~12월 격자)에서 `< >` 는 1년·`<< >>` 는 10년, 연도 보기에서
|
|
15
|
+
* `< >` 는 10년·`<< >>` 는 100년. 일 보기의 폭(한 달·한 해, 일정 캘린더의 주·일 보기는 그 단위)은
|
|
16
|
+
* 호출부가 정한다 — 이 파일은 머리글 모양과 월·연도 격자만 안다.
|
|
17
|
+
*
|
|
18
|
+
* 키보드(월·연도 격자): 화살표(좌우 1칸·상하 4칸), Home/End(그 해·그 10년의 처음·끝), PageUp/PageDown
|
|
19
|
+
* (한 단계 큰 걸음), Enter/Space 고르기, Escape 는 **한 단계 아래 보기로**(연도 → 월 → 호출부의 onExit).
|
|
20
|
+
* Escape 는 여기서 전파를 멈춘다 — 겹 스택(overlay)이 팝오버 자체를 닫아 버리지 않게.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** 머리글·월·연도 보기의 글자. 빠진 자리는 한국어 기본으로 메운다(`resolveCalendarNavLocale`). */
|
|
24
|
+
interface CalendarNavLocale {
|
|
25
|
+
/** 달력 위 연월 캡션 (month는 1–12). */
|
|
26
|
+
formatCaption?: (year: number, month: number) => string;
|
|
27
|
+
/** 월 보기 제목 — '2026년'. */
|
|
28
|
+
formatYear?: (year: number) => string;
|
|
29
|
+
/** 월 보기의 칸 — '3월' (month는 1–12). */
|
|
30
|
+
formatMonth?: (month: number) => string;
|
|
31
|
+
/** 연도 보기 제목 — '2020~2029'(ERP 기간 구분자와 같은 물결). */
|
|
32
|
+
formatDecade?: (from: number, to: number) => string;
|
|
33
|
+
/** 이전/다음 달 버튼의 낭독 라벨 */
|
|
34
|
+
prevMonthLabel?: string;
|
|
35
|
+
nextMonthLabel?: string;
|
|
36
|
+
/** 이전/다음 해 — 일 보기의 `<< >>`, 월 보기의 `< >` */
|
|
37
|
+
prevYearLabel?: string;
|
|
38
|
+
nextYearLabel?: string;
|
|
39
|
+
/** 이전/다음 10년 — 월 보기의 `<< >>`, 연도 보기의 `< >` */
|
|
40
|
+
prevDecadeLabel?: string;
|
|
41
|
+
nextDecadeLabel?: string;
|
|
42
|
+
/** 이전/다음 100년 — 연도 보기의 `<< >>` */
|
|
43
|
+
prevCenturyLabel?: string;
|
|
44
|
+
nextCenturyLabel?: string;
|
|
45
|
+
/** 제목 버튼의 안내(title) — 누르면 넓은 보기로 간다는 뜻 */
|
|
46
|
+
zoomLabel?: string;
|
|
47
|
+
/** 월·연도 격자의 낭독 라벨 */
|
|
48
|
+
monthGridLabel?: string;
|
|
49
|
+
yearGridLabel?: string;
|
|
50
|
+
}
|
|
51
|
+
/** 네 버튼 — 큰 걸음 뒤·작은 걸음 뒤·작은 걸음 앞·큰 걸음 앞. */
|
|
52
|
+
type CalendarNavStep = 'prevBig' | 'prev' | 'next' | 'nextBig';
|
|
53
|
+
|
|
54
|
+
export type { CalendarNavLocale as C, CalendarNavStep as a };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { resolveTreeNodes, TreeView } from './chunk-
|
|
2
|
-
import { OverlayLayer } from './chunk-
|
|
3
|
-
import { Field, Dialog, Icon, Button, Input } from './chunk-
|
|
1
|
+
import { resolveTreeNodes, TreeView } from './chunk-SGR2QDEG.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,4 +1,4 @@
|
|
|
1
|
-
import { formatDisplayValue } from './chunk-
|
|
1
|
+
import { formatDisplayValue } from './chunk-OWM7E55U.js';
|
|
2
2
|
import { mergeStyle, containerLayoutStyle, joinClass, columnsClass, typographyStyle, GAP_CSS } from './chunk-3JTBOABA.js';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Disclosure } from './chunk-
|
|
2
|
-
import { Icon } from './chunk-
|
|
1
|
+
import { Disclosure } from './chunk-P62INTQ5.js';
|
|
2
|
+
import { Icon } from './chunk-OWM7E55U.js';
|
|
3
3
|
import { boxed } from './chunk-3JTBOABA.js';
|
|
4
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
5
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HoistedStyle } from './chunk-RIZRM7OO.js';
|
|
2
|
-
import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-
|
|
3
|
-
import { MOBILE_MEDIA_QUERY } from './chunk-
|
|
4
|
-
import { Icon, Badge, Checkbox, Select, Button } from './chunk-
|
|
2
|
+
import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-ZBX2CAOO.js';
|
|
3
|
+
import { MOBILE_MEDIA_QUERY } from './chunk-TAVKGYH5.js';
|
|
4
|
+
import { Icon, Badge, Checkbox, Select, Button } from './chunk-OWM7E55U.js';
|
|
5
5
|
import { boxed } from './chunk-3JTBOABA.js';
|
|
6
6
|
import { useRef, useEffect, useState, useMemo } from 'react';
|
|
7
7
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Text } from './chunk-
|
|
2
|
-
import { Switch, Field, Select, Input } from './chunk-
|
|
1
|
+
import { Text } from './chunk-5FZBH3T5.js';
|
|
2
|
+
import { Switch, Field, Select, Input } from './chunk-OWM7E55U.js';
|
|
3
3
|
import { boxed } from './chunk-3JTBOABA.js';
|
|
4
4
|
import React, { useMemo } from 'react';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-
|
|
2
|
-
import { Spinner, Badge, Icon } from './chunk-
|
|
1
|
+
import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-ZBX2CAOO.js';
|
|
2
|
+
import { Spinner, Badge, Icon } from './chunk-OWM7E55U.js';
|
|
3
3
|
import { boxed } from './chunk-3JTBOABA.js';
|
|
4
4
|
import { useState, useEffect } from 'react';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { parseJobPositionRow } from './chunk-XRHR452N.js';
|
|
2
|
-
import {
|
|
3
|
-
import { ImageLightbox } from './chunk-
|
|
4
|
-
import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-
|
|
5
|
-
import {
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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
|
-
|
|
961
|
-
|
|
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
|
-
|
|
1137
|
+
const sizeOf = () => flowAuto ? `${el.clientWidth}` : `${el.clientWidth}x${el.clientHeight}`;
|
|
1138
|
+
let last = sizeOf();
|
|
969
1139
|
const observer = new ResizeObserver(() => {
|
|
970
|
-
const size =
|
|
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, ${
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { Textarea, Icon } from './chunk-
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import { OverlayLayer } from './chunk-
|
|
2
|
-
import { Dialog, Button, warnOnce } from './chunk-
|
|
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
|
|