@fmsim/board 2.0.0-beta.109 → 2.0.0-beta.110

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.
@@ -1,3 +1,12 @@
1
+ /**
2
+ * 보드명 다국어의 "현재 언어" 단일 출처.
3
+ *
4
+ * 임베디드(MCS/LV) 환경에서는 호스트가 세션 스토리지 `lv_server_lang` 으로 현재 사용자 언어를
5
+ * 알려주므로 그 값을 권위로 삼는다. `i18next.language` 는 이 앱 자체의 i18n 언어라 호스트 언어와
6
+ * 어긋날 수 있어(보드명만 다른 언어로 표시되는 문제) 쓰지 않는다. `lv_server_lang` 이 없는 단독
7
+ * 실행 등에서만 `i18next.language` 로 폴백한다.
8
+ */
9
+ export declare function getCurrentLang(): string;
1
10
  /** MCS 세션 캐시 ifactI18nObj 의 키 = 지원 언어 목록. 없으면 현재 언어만. */
2
11
  export declare function getSupportedLanguages(): string[];
3
12
  /**
@@ -15,8 +24,14 @@ export declare function stringifyNameI18n(map: Record<string, string>): string |
15
24
  * 1) 정확 일치 2) 구분자 정규화(zh-CN↔zh_CN) 3) 베이스 언어(en-US→en) 4) 키 베이스 일치
16
25
  */
17
26
  export declare function pickLangValue(map: Record<string, string>, lang: string): string;
18
- /** 현재 언어 보드명 → 없으면 대표명(name) 폴백. 보드명 표시/리스팅의 단일 진입점. */
27
+ /**
28
+ * 보드명 표시/리스팅의 단일 진입점.
29
+ *
30
+ * 언어 해석은 이제 백엔드(fmb-service)가 담당한다 — 요청 언어(X-Lang=lv_server_lang)로 계산한
31
+ * `displayName` 을 응답에 실어 주므로, 표시 측은 그 값을 그대로 쓰고(없으면 대표명 name 폴백)
32
+ * 클라이언트에서 nameI18n 맵을 다시 해석하지 않는다. (nameI18n/pickLangValue 는 편집 폼 전용.)
33
+ */
19
34
  export declare function resolveBoardName(board: {
20
35
  name?: string;
21
- nameI18n?: any;
36
+ displayName?: string;
22
37
  } | null | undefined): string;
@@ -8,6 +8,25 @@
8
8
  * @fmsim/board 에 두어 board/fmsim-restful 등 모든 클라이언트 컴포넌트가 공유한다.
9
9
  */
10
10
  import { i18next } from '@operato/i18n';
11
+ /**
12
+ * 보드명 다국어의 "현재 언어" 단일 출처.
13
+ *
14
+ * 임베디드(MCS/LV) 환경에서는 호스트가 세션 스토리지 `lv_server_lang` 으로 현재 사용자 언어를
15
+ * 알려주므로 그 값을 권위로 삼는다. `i18next.language` 는 이 앱 자체의 i18n 언어라 호스트 언어와
16
+ * 어긋날 수 있어(보드명만 다른 언어로 표시되는 문제) 쓰지 않는다. `lv_server_lang` 이 없는 단독
17
+ * 실행 등에서만 `i18next.language` 로 폴백한다.
18
+ */
19
+ export function getCurrentLang() {
20
+ try {
21
+ const lv = sessionStorage.getItem('lv_server_lang');
22
+ if (lv && lv.trim())
23
+ return lv.trim();
24
+ }
25
+ catch (_a) {
26
+ /* sessionStorage 접근 불가 시 폴백 */
27
+ }
28
+ return i18next.language || '';
29
+ }
11
30
  /** MCS 세션 캐시 ifactI18nObj 의 키 = 지원 언어 목록. 없으면 현재 언어만. */
12
31
  export function getSupportedLanguages() {
13
32
  try {
@@ -21,7 +40,7 @@ export function getSupportedLanguages() {
21
40
  catch (_a) {
22
41
  /* 파싱 실패 시 폴백 */
23
42
  }
24
- const cur = i18next.language;
43
+ const cur = getCurrentLang();
25
44
  return cur ? [cur] : [];
26
45
  }
27
46
  /**
@@ -34,7 +53,7 @@ export function languageLabel(code) {
34
53
  if (!c)
35
54
  return '';
36
55
  try {
37
- const dn = new Intl.DisplayNames([i18next.language || 'en'], { type: 'language' });
56
+ const dn = new Intl.DisplayNames([getCurrentLang() || 'en'], { type: 'language' });
38
57
  const name = dn.of(c.replace(/_/g, '-'));
39
58
  if (name && name.toLowerCase() !== c.toLowerCase())
40
59
  return name;
@@ -87,12 +106,16 @@ export function pickLangValue(map, lang) {
87
106
  const hit = Object.keys(map).find(k => k.split(/[-_]/)[0] === base);
88
107
  return (hit && map[hit]) || '';
89
108
  }
90
- /** 현재 언어 보드명 → 없으면 대표명(name) 폴백. 보드명 표시/리스팅의 단일 진입점. */
109
+ /**
110
+ * 보드명 표시/리스팅의 단일 진입점.
111
+ *
112
+ * 언어 해석은 이제 백엔드(fmb-service)가 담당한다 — 요청 언어(X-Lang=lv_server_lang)로 계산한
113
+ * `displayName` 을 응답에 실어 주므로, 표시 측은 그 값을 그대로 쓰고(없으면 대표명 name 폴백)
114
+ * 클라이언트에서 nameI18n 맵을 다시 해석하지 않는다. (nameI18n/pickLangValue 는 편집 폼 전용.)
115
+ */
91
116
  export function resolveBoardName(board) {
92
117
  if (!board)
93
118
  return '';
94
- const map = parseNameI18n(board.nameI18n);
95
- const lang = i18next.language || '';
96
- return pickLangValue(map, lang) || board.name || '';
119
+ return board.displayName || board.name || '';
97
120
  }
98
121
  //# sourceMappingURL=board-i18n.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"board-i18n.js","sourceRoot":"","sources":["../../src/board-i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,yDAAyD;AACzD,MAAM,UAAU,qBAAqB;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QAClD,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;YAC/C,IAAI,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAA;QAC9B,CAAC;IACH,CAAC;IAAC,WAAM,CAAC;QACP,gBAAgB;IAClB,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAA;IAC5B,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7B,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAA;IACjB,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,IAAK,IAAY,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAA;QAC3F,MAAM,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAA;QACxC,IAAI,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,IAAI,CAAA;IACjE,CAAC;IAAC,WAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,aAAa,CAAC,QAAa;IACzC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAA;IACxB,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAA;IACxD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IACnC,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,iBAAiB,CAAC,GAA2B;IAC3D,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IAClE,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAC1E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,GAA2B,EAAE,IAAY;IACrE,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAA;IAC5B,IAAI,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACrC,IAAI,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACpC,IAAI,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAClC,IAAI,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAA;IACnE,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;AAChC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,gBAAgB,CAAC,KAA2D;IAC1F,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAA;IACnC,OAAO,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,EAAE,CAAA;AACrD,CAAC","sourcesContent":["/**\n * 보드명 다국어 유틸 (공유).\n *\n * 서버는 보드의 대표명(name) + 전체 다국어 맵(nameI18n JSON)만 내려주고, \"현재 언어로 무엇을\n * 보여줄지\"는 클라이언트가 정한다. 보드명을 화면에 표시/리스팅하는 모든 곳에서 resolveBoardName 을\n * 사용해야 일관되게 현재 언어 이름(없으면 대표명)으로 표시된다.\n *\n * @fmsim/board 에 두어 board/fmsim-restful 등 모든 클라이언트 컴포넌트가 공유한다.\n */\nimport { i18next } from '@operato/i18n'\n\n/** MCS 세션 캐시 ifactI18nObj 의 키 = 지원 언어 목록. 없으면 현재 언어만. */\nexport function getSupportedLanguages(): string[] {\n try {\n const raw = sessionStorage.getItem('ifactI18nObj')\n if (raw) {\n const keys = Object.keys(JSON.parse(raw) || {})\n if (keys.length) return keys\n }\n } catch {\n /* 파싱 실패 시 폴백 */\n }\n const cur = i18next.language\n return cur ? [cur] : []\n}\n\n/**\n * 언어 코드를 사람이 읽기 쉬운 이름으로 변환한다(표시는 현재 UI 언어).\n * Intl.DisplayNames 로 'en'→English, 'en-US'→American English, 'zh_CN'→Chinese(China) 처럼\n * 같은 언어의 여러 로케일도 구분되게 표기한다. 비표준/해석 불가 코드(예: 'ua')는 코드 그대로.\n */\nexport function languageLabel(code: string): string {\n const c = (code || '').trim()\n if (!c) return ''\n try {\n const dn = new (Intl as any).DisplayNames([i18next.language || 'en'], { type: 'language' })\n const name = dn.of(c.replace(/_/g, '-'))\n if (name && name.toLowerCase() !== c.toLowerCase()) return name\n } catch {\n /* 비표준 코드 등 → 코드 그대로 */\n }\n return c\n}\n\n/** nameI18n(JSON 문자열 또는 객체)을 언어→이름 맵으로 파싱. */\nexport function parseNameI18n(nameI18n: any): Record<string, string> {\n if (!nameI18n) return {}\n if (typeof nameI18n === 'object') return { ...nameI18n }\n try {\n return JSON.parse(nameI18n) || {}\n } catch {\n return {}\n }\n}\n\n/** 채워진 항목만 남긴 맵을 JSON 문자열로. 비어 있으면 undefined(저장 안 함). */\nexport function stringifyNameI18n(map: Record<string, string>): string | undefined {\n const cleaned: Record<string, string> = {}\n for (const [k, v] of Object.entries(map || {})) {\n if (v != null && String(v).trim() !== '') cleaned[k] = String(v)\n }\n return Object.keys(cleaned).length ? JSON.stringify(cleaned) : undefined\n}\n\n/**\n * 언어 맵에서 lang 에 해당하는 값을 관대하게 찾는다. 없으면 ''.\n * 1) 정확 일치 2) 구분자 정규화(zh-CN↔zh_CN) 3) 베이스 언어(en-US→en) 4) 키 베이스 일치\n */\nexport function pickLangValue(map: Record<string, string>, lang: string): string {\n if (!lang || !map) return ''\n if (map[lang]) return map[lang]\n const under = lang.replace(/-/g, '_')\n if (map[under]) return map[under]\n const dash = lang.replace(/_/g, '-')\n if (map[dash]) return map[dash]\n const base = lang.split(/[-_]/)[0]\n if (map[base]) return map[base]\n const hit = Object.keys(map).find(k => k.split(/[-_]/)[0] === base)\n return (hit && map[hit]) || ''\n}\n\n/** 현재 언어 보드명 → 없으면 대표명(name) 폴백. 보드명 표시/리스팅의 단일 진입점. */\nexport function resolveBoardName(board: { name?: string; nameI18n?: any } | null | undefined): string {\n if (!board) return ''\n const map = parseNameI18n(board.nameI18n)\n const lang = i18next.language || ''\n return pickLangValue(map, lang) || board.name || ''\n}\n"]}
1
+ {"version":3,"file":"board-i18n.js","sourceRoot":"","sources":["../../src/board-i18n.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 보드명 다국어 유틸 (공유).\n *\n * 서버는 보드의 대표명(name) + 전체 다국어 맵(nameI18n JSON)만 내려주고, \"현재 언어로 무엇을\n * 보여줄지\"는 클라이언트가 정한다. 보드명을 화면에 표시/리스팅하는 모든 곳에서 resolveBoardName 을\n * 사용해야 일관되게 현재 언어 이름(없으면 대표명)으로 표시된다.\n *\n * @fmsim/board 에 두어 board/fmsim-restful 등 모든 클라이언트 컴포넌트가 공유한다.\n */\nimport { i18next } from '@operato/i18n'\n\n/**\n * 보드명 다국어의 \"현재 언어\" 단일 출처.\n *\n * 임베디드(MCS/LV) 환경에서는 호스트가 세션 스토리지 `lv_server_lang` 으로 현재 사용자 언어를\n * 알려주므로 그 값을 권위로 삼는다. `i18next.language` 는 이 앱 자체의 i18n 언어라 호스트 언어와\n * 어긋날 수 있어(보드명만 다른 언어로 표시되는 문제) 쓰지 않는다. `lv_server_lang` 이 없는 단독\n * 실행 등에서만 `i18next.language` 로 폴백한다.\n */\nexport function getCurrentLang(): string {\n try {\n const lv = sessionStorage.getItem('lv_server_lang')\n if (lv && lv.trim()) return lv.trim()\n } catch {\n /* sessionStorage 접근 불가 시 폴백 */\n }\n return i18next.language || ''\n}\n\n/** MCS 세션 캐시 ifactI18nObj 의 키 = 지원 언어 목록. 없으면 현재 언어만. */\nexport function getSupportedLanguages(): string[] {\n try {\n const raw = sessionStorage.getItem('ifactI18nObj')\n if (raw) {\n const keys = Object.keys(JSON.parse(raw) || {})\n if (keys.length) return keys\n }\n } catch {\n /* 파싱 실패 시 폴백 */\n }\n const cur = getCurrentLang()\n return cur ? [cur] : []\n}\n\n/**\n * 언어 코드를 사람이 읽기 쉬운 이름으로 변환한다(표시는 현재 UI 언어).\n * Intl.DisplayNames 로 'en'→English, 'en-US'→American English, 'zh_CN'→Chinese(China) 처럼\n * 같은 언어의 여러 로케일도 구분되게 표기한다. 비표준/해석 불가 코드(예: 'ua')는 코드 그대로.\n */\nexport function languageLabel(code: string): string {\n const c = (code || '').trim()\n if (!c) return ''\n try {\n const dn = new (Intl as any).DisplayNames([getCurrentLang() || 'en'], { type: 'language' })\n const name = dn.of(c.replace(/_/g, '-'))\n if (name && name.toLowerCase() !== c.toLowerCase()) return name\n } catch {\n /* 비표준 코드 등 → 코드 그대로 */\n }\n return c\n}\n\n/** nameI18n(JSON 문자열 또는 객체)을 언어→이름 맵으로 파싱. */\nexport function parseNameI18n(nameI18n: any): Record<string, string> {\n if (!nameI18n) return {}\n if (typeof nameI18n === 'object') return { ...nameI18n }\n try {\n return JSON.parse(nameI18n) || {}\n } catch {\n return {}\n }\n}\n\n/** 채워진 항목만 남긴 맵을 JSON 문자열로. 비어 있으면 undefined(저장 안 함). */\nexport function stringifyNameI18n(map: Record<string, string>): string | undefined {\n const cleaned: Record<string, string> = {}\n for (const [k, v] of Object.entries(map || {})) {\n if (v != null && String(v).trim() !== '') cleaned[k] = String(v)\n }\n return Object.keys(cleaned).length ? JSON.stringify(cleaned) : undefined\n}\n\n/**\n * 언어 맵에서 lang 에 해당하는 값을 관대하게 찾는다. 없으면 ''.\n * 1) 정확 일치 2) 구분자 정규화(zh-CN↔zh_CN) 3) 베이스 언어(en-US→en) 4) 키 베이스 일치\n */\nexport function pickLangValue(map: Record<string, string>, lang: string): string {\n if (!lang || !map) return ''\n if (map[lang]) return map[lang]\n const under = lang.replace(/-/g, '_')\n if (map[under]) return map[under]\n const dash = lang.replace(/_/g, '-')\n if (map[dash]) return map[dash]\n const base = lang.split(/[-_]/)[0]\n if (map[base]) return map[base]\n const hit = Object.keys(map).find(k => k.split(/[-_]/)[0] === base)\n return (hit && map[hit]) || ''\n}\n\n/**\n * 보드명 표시/리스팅의 단일 진입점.\n *\n * 언어 해석은 이제 백엔드(fmb-service)가 담당한다 — 요청 언어(X-Lang=lv_server_lang)로 계산한\n * `displayName` 을 응답에 실어 주므로, 표시 측은 그 값을 그대로 쓰고(없으면 대표명 name 폴백)\n * 클라이언트에서 nameI18n 맵을 다시 해석하지 않는다. (nameI18n/pickLangValue 는 편집 폼 전용.)\n */\nexport function resolveBoardName(\n board: { name?: string; displayName?: string } | null | undefined\n): string {\n if (!board) return ''\n return board.displayName || board.name || ''\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  export * from './types.js';
2
2
  export { setFlowReplayEnabled, isFlowReplayEnabled } from './config/flow-replay.js';
3
+ export { resolveBoardName, parseNameI18n, stringifyNameI18n, pickLangValue, getSupportedLanguages, languageLabel, getCurrentLang } from './board-i18n.js';
3
4
  export { registerDefaultGroups } from './component/register-default-groups.js';
4
5
  export { DataStorage } from './data-storage/data-storage.js';
5
6
  export { PlaybackEngine } from './playback/playback-engine.js';
package/dist/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './types.js';
2
2
  export { setFlowReplayEnabled, isFlowReplayEnabled } from './config/flow-replay.js';
3
+ export { resolveBoardName, parseNameI18n, stringifyNameI18n, pickLangValue, getSupportedLanguages, languageLabel, getCurrentLang } from './board-i18n.js';
3
4
  export { registerDefaultGroups } from './component/register-default-groups.js';
4
5
  export { DataStorage } from './data-storage/data-storage.js';
5
6
  export { PlaybackEngine } from './playback/playback-engine.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAE1B,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AACnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AAGrE,cAAc,sBAAsB,CAAA;AACpC,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAElC,cAAc,sBAAsB,CAAA","sourcesContent":["export * from './types.js'\n\nexport { setFlowReplayEnabled, isFlowReplayEnabled } from './config/flow-replay.js'\nexport { registerDefaultGroups } from './component/register-default-groups.js'\nexport { DataStorage } from './data-storage/data-storage.js'\nexport { PlaybackEngine } from './playback/playback-engine.js'\nexport { BoardModelCache } from './data-storage/board-model-cache.js'\nexport type { CachedBoard } from './data-storage/board-model-cache.js'\n\nexport * from './ox-board-viewer.js'\nexport * from './ox-board-modeller.js'\nexport * from './ox-board-list.js'\n\nexport * from './component/index.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAE1B,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AACnF,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,cAAc,EACf,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AAGrE,cAAc,sBAAsB,CAAA;AACpC,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAElC,cAAc,sBAAsB,CAAA","sourcesContent":["export * from './types.js'\n\nexport { setFlowReplayEnabled, isFlowReplayEnabled } from './config/flow-replay.js'\nexport {\n resolveBoardName,\n parseNameI18n,\n stringifyNameI18n,\n pickLangValue,\n getSupportedLanguages,\n languageLabel,\n getCurrentLang\n} from './board-i18n.js'\nexport { registerDefaultGroups } from './component/register-default-groups.js'\nexport { DataStorage } from './data-storage/data-storage.js'\nexport { PlaybackEngine } from './playback/playback-engine.js'\nexport { BoardModelCache } from './data-storage/board-model-cache.js'\nexport type { CachedBoard } from './data-storage/board-model-cache.js'\n\nexport * from './ox-board-viewer.js'\nexport * from './ox-board-modeller.js'\nexport * from './ox-board-list.js'\n\nexport * from './component/index.js'\n"]}
@@ -36,13 +36,24 @@ function dispatchFetchLvModelDataByMouseEvent(type, event, target) {
36
36
  return parent || target;
37
37
  }
38
38
  else if (parent && parent.getState('id')) {
39
+ // 클릭된 컴포넌트(target)가 자기 id 없이 id 보유 부모(Machine 등) 안에 배치된 경우 —
40
+ // 예: id 미지정 StockerCapacityBar / ZoneCapacityBar. 이때도 model 은 *실제 클릭된
41
+ // 컴포넌트* 를 실어 model.type 이 자신의 타입이 되게 한다(부모 타입 stocker 로 치환 금지).
42
+ // target 은 자기 id 가 없으므로, 주소가 되는 부모 id/type 은 parentObject 로 실어 위
43
+ // 분기(target 이 id 를 가진 경우)와 payload 형태를 동일하게 맞춘다.
39
44
  window.dispatchEvent(new CustomEvent('fetchLvModelDataByMouseEvent', {
40
45
  detail: {
41
46
  value: {
42
47
  type,
43
48
  event,
44
- model: parent.state,
45
- data: parent.data,
49
+ model: {
50
+ ...target.state,
51
+ parentObject: {
52
+ id: parent.get('id'),
53
+ type: parent.get('type')
54
+ }
55
+ },
56
+ data: target.data,
46
57
  ...(segment ? { segment } : {})
47
58
  }
48
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mcs-mouse-event-handler.js","sourceRoot":"","sources":["../../../src/layers/mcs-mouse-event-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventMap } from '@hatiolab/things-scene'\n\nfunction dispatchFetchLvModelDataByMouseEvent(\n type: 'hover' | 'click' | 'dblclick',\n event: MouseEvent,\n target: Component & { parentObject?: Component }\n): Component | undefined {\n // 플레이백 모드 — 함수 자체를 실행하지 않음.\n // 1) 외부 모니터링 fetchLvModelDataByMouseEvent window 이벤트 차단\n // 2) realTarget 반환 차단 → onclick 의 후속 clickComponent dispatch 가\n // realTarget = undefined 로 흘러 popup 등 후속 backend 호출 경로도 비활성\n // (popup 의 detailInfo() 가드와 더불어 백엔드 요청을 차단하는 1차 게이트.)\n if ((target as any)?.root?.playbackMode) return undefined\n\n const parent = target.parentObject\n\n // CapacityBar(Stocker/Zone) 처럼 세그먼트 게이지를 그리는 컴포넌트는, carrier/abnormal\n // 모드에서 클릭된 세그먼트(Full/Empty/EmptyEmpty/Normal/Abnormal)를 함께 실어 보낸다.\n // capacity 모드·비세그먼트 컴포넌트는 undefined → 필드 미포함.\n const segment = (target as any).segmentAtEvent?.(event)\n\n if (target.getState('id')) {\n window.dispatchEvent(\n new CustomEvent('fetchLvModelDataByMouseEvent', {\n detail: {\n value: {\n type,\n event,\n model: {\n ...target.state,\n parentObject: parent\n ? {\n id: parent.get('id'),\n type: parent.get('type')\n }\n : null\n },\n data: target.data,\n ...(segment ? { segment } : {})\n }\n }\n })\n )\n\n return parent || target\n } else if (parent && parent.getState('id')) {\n window.dispatchEvent(\n new CustomEvent('fetchLvModelDataByMouseEvent', {\n detail: {\n value: {\n type,\n event,\n model: parent.state,\n data: parent.data,\n ...(segment ? { segment } : {})\n }\n }\n })\n )\n\n return parent\n }\n}\n\n// function onmouseenter(event: MouseEvent, hint: any) {\n// const target = hint.origin\n\n// event.stopPropagation()\n\n// dispatchFetchLvModelDataByMouseEvent('hover', event, target)\n// }\n\nfunction onclick(event: MouseEvent, hint: any) {\n const target = hint.origin\n\n event.stopPropagation()\n\n setTimeout(() => {\n if (target.currentEvent !== 'dblclick') {\n const realTarget = dispatchFetchLvModelDataByMouseEvent('click', event, target)\n\n window.dispatchEvent(\n new CustomEvent('clickComponent', {\n detail: realTarget || target\n })\n )\n }\n }, 300)\n}\n\nfunction ondblclick(event: MouseEvent, hint: any) {\n const target = hint.origin\n\n setTimeout(() => {\n target.currentEvent = ''\n }, 500)\n\n event.stopPropagation()\n\n target.currentEvent = 'dblclick'\n\n dispatchFetchLvModelDataByMouseEvent('dblclick', event, target)\n}\n\nEventMap.register('mcs-event-handler', {\n 'model-layer': {\n '(all)': {\n // mouseenter: onmouseenter, /* 사용되지 않고 있음 */\n click: onclick,\n dblclick: ondblclick\n }\n }\n})\n"]}
1
+ {"version":3,"file":"mcs-mouse-event-handler.js","sourceRoot":"","sources":["../../../src/layers/mcs-mouse-event-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventMap } from '@hatiolab/things-scene'\n\nfunction dispatchFetchLvModelDataByMouseEvent(\n type: 'hover' | 'click' | 'dblclick',\n event: MouseEvent,\n target: Component & { parentObject?: Component }\n): Component | undefined {\n // 플레이백 모드 — 함수 자체를 실행하지 않음.\n // 1) 외부 모니터링 fetchLvModelDataByMouseEvent window 이벤트 차단\n // 2) realTarget 반환 차단 → onclick 의 후속 clickComponent dispatch 가\n // realTarget = undefined 로 흘러 popup 등 후속 backend 호출 경로도 비활성\n // (popup 의 detailInfo() 가드와 더불어 백엔드 요청을 차단하는 1차 게이트.)\n if ((target as any)?.root?.playbackMode) return undefined\n\n const parent = target.parentObject\n\n // CapacityBar(Stocker/Zone) 처럼 세그먼트 게이지를 그리는 컴포넌트는, carrier/abnormal\n // 모드에서 클릭된 세그먼트(Full/Empty/EmptyEmpty/Normal/Abnormal)를 함께 실어 보낸다.\n // capacity 모드·비세그먼트 컴포넌트는 undefined → 필드 미포함.\n const segment = (target as any).segmentAtEvent?.(event)\n\n if (target.getState('id')) {\n window.dispatchEvent(\n new CustomEvent('fetchLvModelDataByMouseEvent', {\n detail: {\n value: {\n type,\n event,\n model: {\n ...target.state,\n parentObject: parent\n ? {\n id: parent.get('id'),\n type: parent.get('type')\n }\n : null\n },\n data: target.data,\n ...(segment ? { segment } : {})\n }\n }\n })\n )\n\n return parent || target\n } else if (parent && parent.getState('id')) {\n // 클릭된 컴포넌트(target)가 자기 id 없이 id 보유 부모(Machine 등) 안에 배치된 경우 —\n // 예: id 미지정 StockerCapacityBar / ZoneCapacityBar. 이때도 model 은 *실제 클릭된\n // 컴포넌트* 를 실어 model.type 이 자신의 타입이 되게 한다(부모 타입 stocker 로 치환 금지).\n // target 은 자기 id 가 없으므로, 주소가 되는 부모 id/type 은 parentObject 로 실어 위\n // 분기(target 이 id 를 가진 경우)와 payload 형태를 동일하게 맞춘다.\n window.dispatchEvent(\n new CustomEvent('fetchLvModelDataByMouseEvent', {\n detail: {\n value: {\n type,\n event,\n model: {\n ...target.state,\n parentObject: {\n id: parent.get('id'),\n type: parent.get('type')\n }\n },\n data: target.data,\n ...(segment ? { segment } : {})\n }\n }\n })\n )\n\n return parent\n }\n}\n\n// function onmouseenter(event: MouseEvent, hint: any) {\n// const target = hint.origin\n\n// event.stopPropagation()\n\n// dispatchFetchLvModelDataByMouseEvent('hover', event, target)\n// }\n\nfunction onclick(event: MouseEvent, hint: any) {\n const target = hint.origin\n\n event.stopPropagation()\n\n setTimeout(() => {\n if (target.currentEvent !== 'dblclick') {\n const realTarget = dispatchFetchLvModelDataByMouseEvent('click', event, target)\n\n window.dispatchEvent(\n new CustomEvent('clickComponent', {\n detail: realTarget || target\n })\n )\n }\n }, 300)\n}\n\nfunction ondblclick(event: MouseEvent, hint: any) {\n const target = hint.origin\n\n setTimeout(() => {\n target.currentEvent = ''\n }, 500)\n\n event.stopPropagation()\n\n target.currentEvent = 'dblclick'\n\n dispatchFetchLvModelDataByMouseEvent('dblclick', event, target)\n}\n\nEventMap.register('mcs-event-handler', {\n 'model-layer': {\n '(all)': {\n // mouseenter: onmouseenter, /* 사용되지 않고 있음 */\n click: onclick,\n dblclick: ondblclick\n }\n }\n})\n"]}
@@ -9,6 +9,7 @@ import { customElement, property, state } from 'lit/decorators.js';
9
9
  import { i18next } from '@operato/i18n';
10
10
  import { openPopup } from '@operato/layout';
11
11
  import { fetchBoardNameById } from '@fmsim/api';
12
+ import { resolveBoardName } from './board-i18n.js';
12
13
  let BoardSelector = class BoardSelector extends LitElement {
13
14
  static get styles() {
14
15
  return [
@@ -55,7 +56,8 @@ let BoardSelector = class BoardSelector extends LitElement {
55
56
  }
56
57
  async _resolveName(id) {
57
58
  try {
58
- this.boardName = await fetchBoardNameById(id);
59
+ // 표시명은 백엔드가 요청 언어로 해석해 내려준다(fetchBoardNameById → displayName).
60
+ this.boardName = (await fetchBoardNameById(id)) || id;
59
61
  }
60
62
  catch (_a) {
61
63
  this.boardName = id;
@@ -86,7 +88,7 @@ let BoardSelector = class BoardSelector extends LitElement {
86
88
  @board-selected=${async (e) => {
87
89
  var board = e.detail.board;
88
90
  this.value = board.id;
89
- this.boardName = board.name;
91
+ this.boardName = resolveBoardName(board);
90
92
  this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
91
93
  this.popup && this.popup.close();
92
94
  }}
@@ -1 +1 @@
1
- {"version":3,"file":"ox-editor-board-selector.js","sourceRoot":"","sources":["../../src/ox-editor-board-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@material/web/icon/icon.js'\nimport './selector/ox-board-selector'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { fetchBoardNameById } from '@fmsim/api'\n\n@customElement('ox-editor-board-selector')\nexport class BoardSelector extends LitElement {\n static get styles() {\n return [\n css`\n :host {\n position: relative;\n display: inline-block;\n }\n\n input[type='hidden'] {\n display: none;\n }\n\n .readonly-display {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border: 1px solid rgba(0, 0, 0, 0.2);\n padding: 4px;\n }\n\n #display {\n display: flex;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n height: 24px;\n align-items: center;\n font-size: small;\n }\n\n md-icon {\n position: absolute;\n top: 0;\n right: 0;\n }\n `\n ]\n }\n\n @property({ type: String }) value?: string\n @property({ type: Object }) properties?: any\n\n @state() private boardName?: string\n\n private popup: any\n\n async firstUpdated() {\n if (this.value) {\n await this._resolveName(this.value)\n }\n }\n\n private async _resolveName(id: string) {\n try {\n this.boardName = await fetchBoardNameById(id)\n } catch {\n this.boardName = id\n }\n }\n\n render() {\n return html`\n <input id=\"text\" type=\"hidden\" .value=${this.value || ''} @change=${(e: Event) => this._onInputChanged(e)} />\n <div id=\"display\" class=\"readonly-display\">${this.boardName || this.value || ''}</div>\n\n <md-icon @click=${(e: MouseEvent) => this.openSelector()}>dashboard</md-icon>\n `\n }\n\n _onInputChanged(e: Event) {\n this.value = (e.target as any)?.value\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n\n openSelector() {\n if (this.popup) {\n delete this.popup\n }\n\n var value = this.value || ''\n\n var template = html`\n <ox-board-selector\n .creatable=${true}\n .value=${value}\n @board-selected=${async (e: CustomEvent) => {\n var board = e.detail.board\n this.value = board.id\n this.boardName = board.name\n\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n\n this.popup && this.popup.close()\n }}\n ></ox-board-selector>\n `\n\n this.popup = openPopup(template, {\n backdrop: true,\n size: 'large',\n title: i18next.t('title.select board')\n })\n }\n}\n"]}
1
+ {"version":3,"file":"ox-editor-board-selector.js","sourceRoot":"","sources":["../../src/ox-editor-board-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@material/web/icon/icon.js'\nimport './selector/ox-board-selector'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { fetchBoardNameById } from '@fmsim/api'\nimport { resolveBoardName } from './board-i18n.js'\n\n@customElement('ox-editor-board-selector')\nexport class BoardSelector extends LitElement {\n static get styles() {\n return [\n css`\n :host {\n position: relative;\n display: inline-block;\n }\n\n input[type='hidden'] {\n display: none;\n }\n\n .readonly-display {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border: 1px solid rgba(0, 0, 0, 0.2);\n padding: 4px;\n }\n\n #display {\n display: flex;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n height: 24px;\n align-items: center;\n font-size: small;\n }\n\n md-icon {\n position: absolute;\n top: 0;\n right: 0;\n }\n `\n ]\n }\n\n @property({ type: String }) value?: string\n @property({ type: Object }) properties?: any\n\n @state() private boardName?: string\n\n private popup: any\n\n async firstUpdated() {\n if (this.value) {\n await this._resolveName(this.value)\n }\n }\n\n private async _resolveName(id: string) {\n try {\n // 표시명은 백엔드가 요청 언어로 해석해 내려준다(fetchBoardNameById → displayName).\n this.boardName = (await fetchBoardNameById(id)) || id\n } catch {\n this.boardName = id\n }\n }\n\n render() {\n return html`\n <input id=\"text\" type=\"hidden\" .value=${this.value || ''} @change=${(e: Event) => this._onInputChanged(e)} />\n <div id=\"display\" class=\"readonly-display\">${this.boardName || this.value || ''}</div>\n\n <md-icon @click=${(e: MouseEvent) => this.openSelector()}>dashboard</md-icon>\n `\n }\n\n _onInputChanged(e: Event) {\n this.value = (e.target as any)?.value\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n\n openSelector() {\n if (this.popup) {\n delete this.popup\n }\n\n var value = this.value || ''\n\n var template = html`\n <ox-board-selector\n .creatable=${true}\n .value=${value}\n @board-selected=${async (e: CustomEvent) => {\n var board = e.detail.board\n this.value = board.id\n this.boardName = resolveBoardName(board)\n\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n\n this.popup && this.popup.close()\n }}\n ></ox-board-selector>\n `\n\n this.popup = openPopup(template, {\n backdrop: true,\n size: 'large',\n title: i18next.t('title.select board')\n })\n }\n}\n"]}
@@ -6,6 +6,7 @@ import { i18next, localize } from '@operato/i18n';
6
6
  import { ScrollbarStyles } from '@operato/styles';
7
7
  import InfiniteScrollable from '@operato/utils/mixins/infinite-scrollable.js';
8
8
  import { createBoard, fetchBoardList, fetchGroupList } from '@fmsim/api';
9
+ import { resolveBoardName } from '../board-i18n.js';
9
10
  let BoardSelector = class BoardSelector extends InfiniteScrollable(localize(i18next)(LitElement)) {
10
11
  static get styles() {
11
12
  return [
@@ -144,7 +145,7 @@ let BoardSelector = class BoardSelector extends InfiniteScrollable(localize(i18n
144
145
  ${this.boards.map(board => html `
145
146
  <div class="card" @click=${(e) => this.onClickSelect(board)} ?selected=${this.value === board.id}>
146
147
  <img src=${board.thumbnail} />
147
- <div class="name">${board.name}</div>
148
+ <div class="name">${resolveBoardName(board)}</div>
148
149
  <div class="description">${board.description}</div>
149
150
  </div>
150
151
  `)}
@@ -1 +1 @@
1
- {"version":3,"file":"ox-board-selector.js","sourceRoot":"","sources":["../../../src/selector/ox-board-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import './ox-board-creation-card'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport InfiniteScrollable from '@operato/utils/mixins/infinite-scrollable.js'\nimport { createBoard, fetchBoardList, fetchGroupList } from '@fmsim/api'\n\n@customElement('ox-board-selector')\nexport class BoardSelector extends InfiniteScrollable(localize(i18next)(LitElement)) {\n static get styles() {\n return [\n ScrollbarStyles,\n css`\n :host {\n display: grid;\n grid-template-rows: auto auto 1fr;\n overflow: hidden;\n background-color: var(--popup-content-background-color);\n }\n\n #main {\n overflow: auto;\n padding: var(--popup-content-padding);\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n grid-auto-rows: var(--card-list-rows-height);\n grid-gap: 20px;\n }\n\n .card {\n display: flex;\n flex-direction: column;\n align-items: center;\n overflow: hidden;\n border-radius: var(--card-list-border-radius);\n background-color: var(--card-list-background-color);\n }\n\n .card[selected] {\n border: 3px solid var(--md-sys-color-primary, #6750a4);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--md-sys-color-primary, #6750a4) 30%, transparent), 0 4px 12px rgba(0, 0, 0, 0.25);\n transform: scale(1.02);\n transition: all 0.2s ease;\n }\n\n .card.create {\n overflow: visible;\n }\n\n .card:hover {\n cursor: pointer;\n }\n\n .card > .name {\n background-color: var(--board-renderer-name-background-color);\n opacity: 0.8;\n margin-top: -35px;\n width: 100%;\n color: #fff;\n font-weight: bolder;\n font-size: 13px;\n text-indent: 7px;\n }\n\n .card > .description {\n background-color: rgba(0, 0, 0, 0.7);\n width: 100%;\n min-height: 15px;\n font-size: 0.6rem;\n color: #fff;\n text-indent: 7px;\n }\n .card img {\n max-height: 100%;\n min-height: 100%;\n }\n\n #filter {\n padding: var(--popup-content-padding);\n background-color: #fff;\n box-shadow: var(--box-shadow);\n }\n\n #filter > div {\n float: right;\n margin-left: 10px;\n }\n\n #filter * {\n font-size: 15px;\n }\n\n select {\n text-transform: capitalize;\n }\n `\n ]\n }\n\n @property({ type: Array }) groups: { id: string; name: string; description: string }[] = []\n @property({ type: Array }) boards: any[] = []\n @property({ type: String }) group?: string\n // @property({ type: String }) inherited?: InheritedValueType = InheritedValueType.Include\n @property({ type: Boolean }) creatable: boolean = false\n @property({ type: String }) value?: string\n\n private _page: number = 1\n private _total: number = 0\n\n constructor() {\n super()\n\n this._infiniteScrollOptions.limit = 50\n }\n\n render() {\n return html`\n <div id=\"filter\">\n <div>\n <label for=\"group\">Group</label>\n <select\n id=\"group\"\n @change=${(e: Event) => {\n this.group = (e.currentTarget as any)?.value\n this.requestUpdate()\n }}\n >\n <option value=\"\">${i18next.t('label.all')}</option>\n ${this.groups.map(group => html` <option value=${group.id}>${group.description}</option> `)}\n </select>\n </div>\n </div>\n\n <div\n id=\"main\"\n @scroll=${(e: Event) => {\n this.onScroll(e)\n }}\n >\n ${this.creatable\n ? html`\n <ox-board-creation-card\n class=\"card create\"\n .groups=${this.groups}\n .defaultGroup=${this.group}\n @create-board=${(e: CustomEvent) => this.onCreateBoard(e)}\n ></ox-board-creation-card>\n `\n : html``}\n ${this.boards.map(\n board => html`\n <div class=\"card\" @click=${(e: Event) => this.onClickSelect(board)} ?selected=${this.value === board.id}>\n <img src=${board.thumbnail} />\n <div class=\"name\">${board.name}</div>\n <div class=\"description\">${board.description}</div>\n </div>\n `\n )}\n </div>\n `\n }\n\n get scrollTargetEl(): HTMLElement | null {\n return this.renderRoot.querySelector('#main') as HTMLElement\n }\n\n async scrollAction() {\n return this.appendBoards()\n }\n\n firstUpdated() {\n this.refreshGroups()\n }\n\n async updated(changed: PropertyValues<this>) {\n if (changed.has('group')) {\n this.refreshBoards()\n }\n\n // 선택된 카드로 자동 스크롤\n if (changed.has('boards') || changed.has('value')) {\n await this.updateComplete\n const selected = this.renderRoot.querySelector('.card[selected]') as HTMLElement\n if (selected) {\n selected.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }\n }\n }\n\n onClickSelect(board: any) {\n this.dispatchEvent(\n new CustomEvent('board-selected', {\n composed: true,\n bubbles: true,\n detail: {\n board\n }\n })\n )\n }\n\n async onCreateBoard(e: CustomEvent) {\n const { name, description, groupId } = e.detail\n\n await this.createBoard(name, description, groupId)\n this.refreshBoards()\n }\n\n async refreshGroups() {\n const fetched = await fetchGroupList()\n\n const groups = 'groups' in fetched /* graphql */ ? fetched.groups.items : fetched.data.groups\n\n this.group = groups.filter((group: { id: string }) => group.id == this.group).length > 0 ? this.group : ''\n }\n\n async refreshBoards() {\n const boards = await this.getBoards()\n this.boards = [...boards]\n\n const creationCard = this.renderRoot.querySelector('ox-board-creation-card')\n if (creationCard) {\n ;(creationCard as any).reset()\n }\n }\n\n async appendBoards() {\n const boards = await this.getBoards({ page: this._page + 1 })\n this.boards = [...this.boards, ...boards]\n }\n\n async getBoards({ page = 1, limit = this._infiniteScrollOptions.limit } = {}) {\n const filters = []\n const sortings = [] as any[]\n const pagination = {\n limit,\n page\n }\n\n if (this.group)\n filters.push({\n name: 'groupId',\n operator: 'eq',\n value: this.group\n })\n\n const variables = {\n filters,\n sortings,\n pagination\n // inherited: this.inherited || InheritedValueType.Include\n }\n\n const fetched = await fetchBoardList(variables)\n\n if ('boards' in fetched /* graphql */) {\n const { total, items } = fetched.boards\n\n this._total = total\n this._page = page\n\n return items\n } else {\n const { boards } = fetched.data\n this._total = boards.length\n this._page = 1\n\n return boards\n }\n }\n\n async createBoard(name: string, description: string, groupId: string) {\n const model = JSON.stringify({\n width: 1600,\n height: 1200,\n fillStyle: 'white'\n })\n\n const { data } = await createBoard({\n name,\n description,\n groupId,\n model\n })\n\n return data\n }\n}\n"]}
1
+ {"version":3,"file":"ox-board-selector.js","sourceRoot":"","sources":["../../../src/selector/ox-board-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import './ox-board-creation-card'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport InfiniteScrollable from '@operato/utils/mixins/infinite-scrollable.js'\nimport { createBoard, fetchBoardList, fetchGroupList } from '@fmsim/api'\nimport { resolveBoardName } from '../board-i18n.js'\n\n@customElement('ox-board-selector')\nexport class BoardSelector extends InfiniteScrollable(localize(i18next)(LitElement)) {\n static get styles() {\n return [\n ScrollbarStyles,\n css`\n :host {\n display: grid;\n grid-template-rows: auto auto 1fr;\n overflow: hidden;\n background-color: var(--popup-content-background-color);\n }\n\n #main {\n overflow: auto;\n padding: var(--popup-content-padding);\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n grid-auto-rows: var(--card-list-rows-height);\n grid-gap: 20px;\n }\n\n .card {\n display: flex;\n flex-direction: column;\n align-items: center;\n overflow: hidden;\n border-radius: var(--card-list-border-radius);\n background-color: var(--card-list-background-color);\n }\n\n .card[selected] {\n border: 3px solid var(--md-sys-color-primary, #6750a4);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--md-sys-color-primary, #6750a4) 30%, transparent), 0 4px 12px rgba(0, 0, 0, 0.25);\n transform: scale(1.02);\n transition: all 0.2s ease;\n }\n\n .card.create {\n overflow: visible;\n }\n\n .card:hover {\n cursor: pointer;\n }\n\n .card > .name {\n background-color: var(--board-renderer-name-background-color);\n opacity: 0.8;\n margin-top: -35px;\n width: 100%;\n color: #fff;\n font-weight: bolder;\n font-size: 13px;\n text-indent: 7px;\n }\n\n .card > .description {\n background-color: rgba(0, 0, 0, 0.7);\n width: 100%;\n min-height: 15px;\n font-size: 0.6rem;\n color: #fff;\n text-indent: 7px;\n }\n .card img {\n max-height: 100%;\n min-height: 100%;\n }\n\n #filter {\n padding: var(--popup-content-padding);\n background-color: #fff;\n box-shadow: var(--box-shadow);\n }\n\n #filter > div {\n float: right;\n margin-left: 10px;\n }\n\n #filter * {\n font-size: 15px;\n }\n\n select {\n text-transform: capitalize;\n }\n `\n ]\n }\n\n @property({ type: Array }) groups: { id: string; name: string; description: string }[] = []\n @property({ type: Array }) boards: any[] = []\n @property({ type: String }) group?: string\n // @property({ type: String }) inherited?: InheritedValueType = InheritedValueType.Include\n @property({ type: Boolean }) creatable: boolean = false\n @property({ type: String }) value?: string\n\n private _page: number = 1\n private _total: number = 0\n\n constructor() {\n super()\n\n this._infiniteScrollOptions.limit = 50\n }\n\n render() {\n return html`\n <div id=\"filter\">\n <div>\n <label for=\"group\">Group</label>\n <select\n id=\"group\"\n @change=${(e: Event) => {\n this.group = (e.currentTarget as any)?.value\n this.requestUpdate()\n }}\n >\n <option value=\"\">${i18next.t('label.all')}</option>\n ${this.groups.map(group => html` <option value=${group.id}>${group.description}</option> `)}\n </select>\n </div>\n </div>\n\n <div\n id=\"main\"\n @scroll=${(e: Event) => {\n this.onScroll(e)\n }}\n >\n ${this.creatable\n ? html`\n <ox-board-creation-card\n class=\"card create\"\n .groups=${this.groups}\n .defaultGroup=${this.group}\n @create-board=${(e: CustomEvent) => this.onCreateBoard(e)}\n ></ox-board-creation-card>\n `\n : html``}\n ${this.boards.map(\n board => html`\n <div class=\"card\" @click=${(e: Event) => this.onClickSelect(board)} ?selected=${this.value === board.id}>\n <img src=${board.thumbnail} />\n <div class=\"name\">${resolveBoardName(board)}</div>\n <div class=\"description\">${board.description}</div>\n </div>\n `\n )}\n </div>\n `\n }\n\n get scrollTargetEl(): HTMLElement | null {\n return this.renderRoot.querySelector('#main') as HTMLElement\n }\n\n async scrollAction() {\n return this.appendBoards()\n }\n\n firstUpdated() {\n this.refreshGroups()\n }\n\n async updated(changed: PropertyValues<this>) {\n if (changed.has('group')) {\n this.refreshBoards()\n }\n\n // 선택된 카드로 자동 스크롤\n if (changed.has('boards') || changed.has('value')) {\n await this.updateComplete\n const selected = this.renderRoot.querySelector('.card[selected]') as HTMLElement\n if (selected) {\n selected.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }\n }\n }\n\n onClickSelect(board: any) {\n this.dispatchEvent(\n new CustomEvent('board-selected', {\n composed: true,\n bubbles: true,\n detail: {\n board\n }\n })\n )\n }\n\n async onCreateBoard(e: CustomEvent) {\n const { name, description, groupId } = e.detail\n\n await this.createBoard(name, description, groupId)\n this.refreshBoards()\n }\n\n async refreshGroups() {\n const fetched = await fetchGroupList()\n\n const groups = 'groups' in fetched /* graphql */ ? fetched.groups.items : fetched.data.groups\n\n this.group = groups.filter((group: { id: string }) => group.id == this.group).length > 0 ? this.group : ''\n }\n\n async refreshBoards() {\n const boards = await this.getBoards()\n this.boards = [...boards]\n\n const creationCard = this.renderRoot.querySelector('ox-board-creation-card')\n if (creationCard) {\n ;(creationCard as any).reset()\n }\n }\n\n async appendBoards() {\n const boards = await this.getBoards({ page: this._page + 1 })\n this.boards = [...this.boards, ...boards]\n }\n\n async getBoards({ page = 1, limit = this._infiniteScrollOptions.limit } = {}) {\n const filters = []\n const sortings = [] as any[]\n const pagination = {\n limit,\n page\n }\n\n if (this.group)\n filters.push({\n name: 'groupId',\n operator: 'eq',\n value: this.group\n })\n\n const variables = {\n filters,\n sortings,\n pagination\n // inherited: this.inherited || InheritedValueType.Include\n }\n\n const fetched = await fetchBoardList(variables)\n\n if ('boards' in fetched /* graphql */) {\n const { total, items } = fetched.boards\n\n this._total = total\n this._page = page\n\n return items\n } else {\n const { boards } = fetched.data\n this._total = boards.length\n this._page = 1\n\n return boards\n }\n }\n\n async createBoard(name: string, description: string, groupId: string) {\n const model = JSON.stringify({\n width: 1600,\n height: 1200,\n fillStyle: 'white'\n })\n\n const { data } = await createBoard({\n name,\n description,\n groupId,\n model\n })\n\n return data\n }\n}\n"]}