gnui 1.2.16 → 1.2.18

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 (78) hide show
  1. package/dist/js/gnui.esm.js +772 -111
  2. package/dist/js/gnui.js +772 -111
  3. package/dist/js/gnui.min.js +6 -6
  4. package/dist/styles/default.css +1018 -108
  5. package/dist/styles/gpi.css +1018 -108
  6. package/dist/styles/green24.css +1229 -289
  7. package/dist/styles/insights.css +1018 -108
  8. package/dist/styles/nac.css +1019 -109
  9. package/dist/styles/ztnac.css +1205 -265
  10. package/package.json +2 -2
  11. package/styleguide/assets/components.js +216 -9
  12. package/styleguide/assets/js/gnui.js +772 -111
  13. package/styleguide/assets/js/gnui.min.js +6 -6
  14. package/styleguide/assets/styles/default.css +1018 -108
  15. package/styleguide/assets/styles/gpi.css +1018 -108
  16. package/styleguide/assets/styles/green24.css +1229 -289
  17. package/styleguide/assets/styles/insights.css +1018 -108
  18. package/styleguide/assets/styles/nac.css +1019 -109
  19. package/styleguide/assets/styles/ztnac.css +1205 -265
  20. package/styleguide/category/COLOR/index.html +2 -2
  21. package/styleguide/category/COMPONENT/Alert(js)/index.html +2 -2
  22. package/styleguide/category/COMPONENT/Bignumber/index.html +2 -2
  23. package/styleguide/category/COMPONENT/Breadcrumb/index.html +2 -2
  24. package/styleguide/category/COMPONENT/Calendar(js)/index.html +2 -2
  25. package/styleguide/category/COMPONENT/Card/index.html +2 -2
  26. package/styleguide/category/COMPONENT/Chart(js)/index.html +2 -2
  27. package/styleguide/category/COMPONENT/Datagrid(js)/index.html +136 -9
  28. package/styleguide/category/COMPONENT/Datalist(js)/index.html +2 -2
  29. package/styleguide/category/COMPONENT/Growl(js)/index.html +2 -2
  30. package/styleguide/category/COMPONENT/JsonView(js)/index.html +2 -2
  31. package/styleguide/category/COMPONENT/Loader(js)/index.html +21 -4
  32. package/styleguide/category/COMPONENT/MenuButton(js)/index.html +74 -6
  33. package/styleguide/category/COMPONENT/Message(js)/index.html +2 -2
  34. package/styleguide/category/COMPONENT/Modal(js)/index.html +2 -2
  35. package/styleguide/category/COMPONENT/Pagination(js)/index.html +2 -2
  36. package/styleguide/category/COMPONENT/Panel/index.html +2 -2
  37. package/styleguide/category/COMPONENT/Progressbar(js)/index.html +2 -2
  38. package/styleguide/category/COMPONENT/Tab(js)/index.html +2 -2
  39. package/styleguide/category/COMPONENT/Tagcloud(js)/index.html +2 -2
  40. package/styleguide/category/COMPONENT/Tooltip(js)/index.html +2 -2
  41. package/styleguide/category/COMPONENT/Tree(js)/index.html +2 -2
  42. package/styleguide/category/CONTROLS/Button(js)/index.html +2 -2
  43. package/styleguide/category/CONTROLS/Checkbox/index.html +2 -2
  44. package/styleguide/category/CONTROLS/Colorpicker(js)/index.html +2 -2
  45. package/styleguide/category/CONTROLS/Datepicker(js)/index.html +2 -2
  46. package/styleguide/category/CONTROLS/Dropdown(js)/index.html +2 -2
  47. package/styleguide/category/CONTROLS/File/index.html +2 -2
  48. package/styleguide/category/CONTROLS/Form/Control/index.html +2 -2
  49. package/styleguide/category/CONTROLS/Form/Field/index.html +2 -2
  50. package/styleguide/category/CONTROLS/Form/Plain/index.html +2 -2
  51. package/styleguide/category/CONTROLS/Input/index.html +2 -2
  52. package/styleguide/category/CONTROLS/MultiText(js)/index.html +2 -2
  53. package/styleguide/category/CONTROLS/Picklist(js)/index.html +28 -18
  54. package/styleguide/category/CONTROLS/Radio/index.html +2 -2
  55. package/styleguide/category/CONTROLS/Select/index.html +2 -2
  56. package/styleguide/category/CONTROLS/SelectButton(js)/index.html +2 -2
  57. package/styleguide/category/CONTROLS/Slider/index.html +2 -2
  58. package/styleguide/category/CONTROLS/SortableList(js)/index.html +487 -0
  59. package/styleguide/category/CONTROLS/Switch(js)/index.html +2 -2
  60. package/styleguide/category/CONTROLS/SyntaxInput(js)/index.html +2 -2
  61. package/styleguide/category/CONTROLS/Textarea/index.html +2 -2
  62. package/styleguide/category/CONTROLS/Time(js)/index.html +2 -2
  63. package/styleguide/category/ELEMENTS/Box/index.html +2 -2
  64. package/styleguide/category/ELEMENTS/Icon/index.html +2 -2
  65. package/styleguide/category/ELEMENTS/Image/index.html +2 -2
  66. package/styleguide/category/ELEMENTS/List/index.html +2 -2
  67. package/styleguide/category/ELEMENTS/Table/index.html +2 -2
  68. package/styleguide/category/ELEMENTS/Tag/index.html +2 -2
  69. package/styleguide/category/ELEMENTS/Title/index.html +2 -2
  70. package/styleguide/category/LAYOUT/Container/index.html +2 -2
  71. package/styleguide/category/LAYOUT/Grid/index.html +2 -2
  72. package/styleguide/category/LAYOUT/Splitter(js)/index.html +2 -2
  73. package/styleguide/category/UTILITY/index.html +2 -2
  74. package/styleguide/category/Utils/index.html +2 -2
  75. package/styleguide/color.html +2 -2
  76. package/styleguide/index.html +2 -2
  77. package/styleguide/tag/javascript/index.html +608 -31
  78. package/styleguide/tag/v.0.1.0/index.html +608 -31
@@ -14112,15 +14112,15 @@ function findValue(obj, pathString) {
14112
14112
  if (!obj || !pathString) {
14113
14113
  return "";
14114
14114
  }
14115
- if (isString(pathString)) {
14116
- pathString = pathString.split(".");
14117
- }
14118
- const currentPath = pathString.splice(0, 1)[0];
14119
- if (obj[currentPath] !== void 0) {
14120
- return pathString.length ? findValue(obj[currentPath], pathString) : obj[currentPath];
14121
- } else {
14122
- return "";
14115
+ const keys = isString(pathString) ? pathString.split(".") : pathString;
14116
+ let current = obj;
14117
+ for (const key of keys) {
14118
+ if (current === void 0 || current === null) {
14119
+ return "";
14120
+ }
14121
+ current = current[key];
14123
14122
  }
14123
+ return current !== void 0 ? current : "";
14124
14124
  }
14125
14125
  function findProperty(obj, predicate) {
14126
14126
  let result = [];
@@ -14259,17 +14259,18 @@ function interpolateURL(url, source) {
14259
14259
  return dmustach.test(result) ? interpolateURL(result, source) : result;
14260
14260
  }
14261
14261
  function interpolateCop(textCondition, data2, parent2, $2) {
14262
- const onlymustach = /^\{{2}[^{}]*\}{2}$/;
14262
+ const onlymustach = /^\{{2}[\w.$\[\]]+\}{2}$/;
14263
14263
  if (onlymustach.test(textCondition)) {
14264
14264
  const path = textCondition.replace("{{", "").replace("}}", "");
14265
14265
  return findValue({ data: data2, parent: parent2, $: $2 }, path);
14266
14266
  }
14267
14267
  const dmustach = new RegExp(/\{{([^{}]*)}}/gm);
14268
+ const context = { data: data2, parent: parent2, $: $2 };
14268
14269
  const result = textCondition.replace(dmustach, (match) => {
14269
14270
  const conditionalOp = match.replace(/\{{|\}}/gm, "");
14270
- return new Function("data", "parent", "$", "return " + conditionalOp)(data2, parent2, $2);
14271
+ return new Function("ctx", "const {data, parent, $} = ctx; return " + conditionalOp)(context);
14271
14272
  });
14272
- return dmustach.test(result) ? this.interpolateCop(result, data2, parent2, $2) : result;
14273
+ return dmustach.test(result) ? interpolateCop(result, context.data, context.parent, context.$) : result;
14273
14274
  }
14274
14275
  function parseBundle(text2, locale) {
14275
14276
  let _value = text2;
@@ -14703,7 +14704,10 @@ function apply(element, args, fn) {
14703
14704
  });
14704
14705
  }
14705
14706
  function getArgs(args) {
14706
- return args.reduce((args2, arg) => args2.concat.call(args2, isString(arg) && includes(arg, " ") ? arg.trim().split(" ") : arg), []);
14707
+ return args.reduce(
14708
+ (args2, arg) => args2.concat.call(args2, isString(arg) && includes(arg, " ") ? arg.trim().split(" ") : arg),
14709
+ []
14710
+ );
14707
14711
  }
14708
14712
  var supports = {
14709
14713
  get Multiple() {
@@ -14729,7 +14733,7 @@ function hslToRgb([h, s, l]) {
14729
14733
  s = s / 100;
14730
14734
  l = l / 100;
14731
14735
  const c = (1 - Math.abs(2 * l - 1)) * s, x = c * (1 - Math.abs(h / 60 % 2 - 1)), m = l - c / 2;
14732
- let r, g, b;
14736
+ let r = 0, g = 0, b = 0;
14733
14737
  switch (true) {
14734
14738
  case (h > -1 && h < 60):
14735
14739
  r = c;
@@ -15008,7 +15012,14 @@ function monthData(locale) {
15008
15012
  function dateData(locale) {
15009
15013
  return ["S", "M", "T", "W", "T", "F", "S"];
15010
15014
  }
15011
- function objToDate({ year, month, day, hour, minute, second }) {
15015
+ function objToDate({
15016
+ year,
15017
+ month,
15018
+ day,
15019
+ hour,
15020
+ minute,
15021
+ second
15022
+ }) {
15012
15023
  if (hour === void 0) {
15013
15024
  hour = "00";
15014
15025
  }
@@ -15237,7 +15248,11 @@ function offset(element, coordinates) {
15237
15248
  ["left", "top"].forEach((prop) => {
15238
15249
  if (prop in coordinates) {
15239
15250
  const value = css$1(element, prop);
15240
- css$1(element, prop, coordinates[prop] - (currentOffset == null ? void 0 : currentOffset[prop]) + toFloat(pos === "absolute" && value === "auto" ? position(element)[prop] : value));
15251
+ css$1(
15252
+ element,
15253
+ prop,
15254
+ coordinates[prop] - (currentOffset == null ? void 0 : currentOffset[prop]) + toFloat(pos === "absolute" && value === "auto" ? position(element)[prop] : value)
15255
+ );
15241
15256
  }
15242
15257
  });
15243
15258
  }
@@ -15360,7 +15375,10 @@ function before(ref, element) {
15360
15375
  }
15361
15376
  function after(ref, element) {
15362
15377
  ref = $(ref);
15363
- return insertNodes(element, (element2) => ref.nextSibling ? before(ref.nextSibling, element2) : append(ref.parentNode, element2));
15378
+ return insertNodes(
15379
+ element,
15380
+ (element2) => ref.nextSibling ? before(ref.nextSibling, element2) : append(ref.parentNode, element2)
15381
+ );
15364
15382
  }
15365
15383
  function insertNodes(element, fn) {
15366
15384
  element = isString(element) ? fragment(element) : element;
@@ -15384,13 +15402,21 @@ function on(...args) {
15384
15402
  listener = delegate(targets, selector, listener);
15385
15403
  }
15386
15404
  useCapture = useCaptureFilter(useCapture);
15387
- type.split(" ").forEach((type2) => targets.forEach((target) => target.addEventListener(type2, listener, useCapture)));
15405
+ type.split(" ").forEach(
15406
+ (type2) => targets.forEach(
15407
+ (target) => target.addEventListener(type2, listener, useCapture)
15408
+ )
15409
+ );
15388
15410
  return () => off(targets, type, listener, useCapture);
15389
15411
  }
15390
15412
  function off(targets, type, listener, useCapture = false) {
15391
15413
  useCapture = useCaptureFilter(useCapture);
15392
15414
  targets = toEventTargets(targets);
15393
- type.split(" ").forEach((type2) => targets.forEach((target) => target.removeEventListener(type2, listener, useCapture)));
15415
+ type.split(" ").forEach(
15416
+ (type2) => targets.forEach(
15417
+ (target) => target.removeEventListener(type2, listener, useCapture)
15418
+ )
15419
+ );
15394
15420
  }
15395
15421
  function once(...args) {
15396
15422
  const [element, type, selector, listener, useCapture, condition] = getArgs2(args);
@@ -15410,7 +15436,10 @@ function once(...args) {
15410
15436
  return off2;
15411
15437
  }
15412
15438
  function trigger(targets, event, detail2) {
15413
- return toEventTargets(targets).reduce((notCanceled, target) => notCanceled && target.dispatchEvent(createEvent(event, true, true, detail2)), true);
15439
+ return toEventTargets(targets).reduce(
15440
+ (notCanceled, target) => notCanceled && target.dispatchEvent(createEvent(event, true, true, detail2)),
15441
+ true
15442
+ );
15414
15443
  }
15415
15444
  function createEvent(e, bubbles = true, cancelable = false, detail2) {
15416
15445
  if (isString(e)) {
@@ -16895,7 +16924,10 @@ class GNCoreEventManager {
16895
16924
  // 이벤트 삭제
16896
16925
  delete this._eventMap[uid];
16897
16926
  }
16898
- dispatch(uid, name, params) {
16927
+ /**
16928
+ * lifeCycle 핸들러 실행
16929
+ */
16930
+ cyclepatch(uid, name, params) {
16899
16931
  const _events = this._getEvent(uid, name);
16900
16932
  if (_events.length) {
16901
16933
  _events.forEach((_event) => {
@@ -16904,6 +16936,28 @@ class GNCoreEventManager {
16904
16936
  });
16905
16937
  }
16906
16938
  }
16939
+ /**
16940
+ * 등록된 이벤트 핸들러 실행
16941
+ *
16942
+ * - sync/async 핸들러를 모두 지원한다.
16943
+ * - 모든 핸들러를 순차 실행한 뒤, 하나라도 `false` 를 반환한 경우 `true`(cancelled)를 반환한다.
16944
+ * (첫 false 에서 중단하지 않고, 나머지 핸들러도 계속 실행한다)
16945
+ */
16946
+ async dispatch(uid, name, params) {
16947
+ const _events = this._getEvent(uid, name);
16948
+ let cancelled = false;
16949
+ if (_events.length) {
16950
+ for (const _event of _events) {
16951
+ const _target = _event.target || this;
16952
+ // sync/async 둘 다 지원: Promise.resolve 로 감싸서 await
16953
+ const result = await Promise.resolve(params ? _event.handler.call(_target, ...params) : _event.handler.call(_target));
16954
+ if (result === false) {
16955
+ cancelled = true;
16956
+ }
16957
+ }
16958
+ }
16959
+ return cancelled;
16960
+ }
16907
16961
  _getEvent(uid, name) {
16908
16962
  // parameters에 해당하는 이벤트 반환
16909
16963
  return this._eventMap[uid]
@@ -16988,7 +17042,7 @@ class GNUIState {
16988
17042
  });
16989
17043
  return _findComponent;
16990
17044
  }
16991
- // 등록된 컴포넌트 제거
17045
+ // 등록된 컴포넌트 제거 (selector 기반)
16992
17046
  _removeComponent(selector) {
16993
17047
  if (!selector) {
16994
17048
  return;
@@ -16997,10 +17051,30 @@ class GNUIState {
16997
17051
  Object.values(this._componentMap).forEach(n => {
16998
17052
  // 동일한 selector 인지 비교해서 동일한 component의 selector이면 제거 처리
16999
17053
  if (isEquals(n.selector, _selector)) {
17000
- delete this._componentMap[_selector._uid];
17054
+ // componentMap에서 완전히 제거
17055
+ delete this._componentMap[n.uid];
17056
+ // 컴포넌트 내부 참조도 제거 (메모리 누수 방지)
17057
+ if (n.component) {
17058
+ n.component = null;
17059
+ }
17060
+ n.selector = null;
17001
17061
  }
17002
17062
  });
17003
17063
  }
17064
+ // 등록된 컴포넌트 제거 (uid 기반 - 더 효율적)
17065
+ _removeComponentByUid(uid) {
17066
+ if (!uid || !this._componentMap[uid]) {
17067
+ return;
17068
+ }
17069
+ // 컴포넌트 내부 참조 제거 (메모리 누수 방지)
17070
+ const componentInfo = this._componentMap[uid];
17071
+ if (componentInfo.component) {
17072
+ componentInfo.component = null;
17073
+ }
17074
+ componentInfo.selector = null;
17075
+ // componentMap에서 완전히 제거
17076
+ delete this._componentMap[uid];
17077
+ }
17004
17078
  // 컴포넌트 life cycle에 따른 eventManager dispatch
17005
17079
  _detectedCycle(uid, name) {
17006
17080
  // component 마지막 status 업데이트
@@ -17010,7 +17084,7 @@ class GNUIState {
17010
17084
  // event manager를 이용해 해당 uid 이벤트 dispatch
17011
17085
  const eventManager = GNCoreEventManager.getInstance();
17012
17086
  // 호출 후
17013
- eventManager.dispatch(uid, name, '');
17087
+ eventManager.cyclepatch(uid, name, '');
17014
17088
  // 이벤트 해제 - life cycle 은 컴포넌트 별로 한번씩만 존재하므로..
17015
17089
  eventManager.remove(uid, name);
17016
17090
  }
@@ -17024,6 +17098,7 @@ class GNUIState {
17024
17098
  function _removedNode(removed) {
17025
17099
  Array.prototype.forEach.call(removed, (rm) => {
17026
17100
  // 삭제노드 연관 컴포넌트 (ex. tooltip) 삭제
17101
+ var _a;
17027
17102
  const dependents = findAll('[data-gnui]', rm);
17028
17103
  each(dependents, (dependent) => {
17029
17104
  if (isElement$2(dependent)) {
@@ -17037,7 +17112,7 @@ class GNUIState {
17037
17112
  remove($('#' + attr(rm, 'data-gnui')));
17038
17113
  }
17039
17114
  const findComponent = closerThis._getComponent($(rm));
17040
- if (findComponent && findComponent._uid && !findComponent.$el.parentNode && findComponent.$name !== 'modal') {
17115
+ if (findComponent && findComponent._uid && !((_a = findComponent.$el) === null || _a === void 0 ? void 0 : _a.parentNode) && findComponent.$name !== 'modal') {
17041
17116
  // state manager 에서 component 삭제
17042
17117
  closerThis._removeComponent(rm);
17043
17118
  // event manager 에서 unbind
@@ -17236,25 +17311,48 @@ class GNCoreInstance {
17236
17311
  }
17237
17312
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
17238
17313
  $update(element = this.$el, e) { }
17239
- $event(component, name, ...params) {
17314
+ /**
17315
+ * 컴포넌트 이벤트 디스패치 헬퍼
17316
+ *
17317
+ * - sync/async 핸들러 모두 지원한다.
17318
+ * - 하나 이상의 핸들러에서 `false`를 반환하면 `true`(cancelled) 를 반환한다.
17319
+ * - 단순 알림용(fire-and-forget) 이벤트는 반환값/await 없이 호출해도 된다.
17320
+ */
17321
+ async $event(component, name, ...params) {
17240
17322
  const eventManager = GNCoreEventManager.getInstance();
17241
- eventManager.dispatch(component._uid, name, params);
17323
+ return eventManager.dispatch(component._uid, name, params);
17242
17324
  }
17243
17325
  $destroy(component = this, removeEl = true) {
17326
+ var _a;
17244
17327
  const stateManager = GNUIState.getInstance();
17245
17328
  // state manager 를 통해 destroy 상태 dispatch
17246
17329
  stateManager._detectedCycle(component._uid, 'destroy');
17247
- // remove Component in state manager
17248
- stateManager._removeComponent(component.$selector);
17249
17330
  // remove DOM (by removeEl)
17250
17331
  if (removeEl) {
17251
17332
  style(component.$el, 'display', 'none');
17252
17333
  remove(component.$el);
17253
17334
  }
17335
+ if (((_a = component.$options) === null || _a === void 0 ? void 0 : _a._destroy) && isFunction(component.$options._destroy)) {
17336
+ component.$options._destroy();
17337
+ }
17254
17338
  // state manager 를 통해 destroy 상태 dispatch
17255
17339
  stateManager._detectedCycle(component._uid, 'destroyed');
17256
- // event manager 에서 등록 해제가 가장 마지막..
17340
+ // event manager 에서 등록 해제
17257
17341
  GNCoreEventManager.getInstance().removeAll(component._uid);
17342
+ // state manager에서 component 제거 (uid 기반으로 효율적 제거)
17343
+ stateManager._removeComponentByUid(component._uid);
17344
+ // 메모리 누수 방지: component의 모든 hasOwnProperty 제거
17345
+ // config, events, methods, _hidden 등 동적으로 추가된 속성 포함
17346
+ Object.keys(component).forEach((key) => {
17347
+ try {
17348
+ component[key] = null;
17349
+ delete component[key];
17350
+ }
17351
+ catch (e) {
17352
+ // readonly 속성 등 삭제 불가능한 경우 무시
17353
+ }
17354
+ });
17355
+ component = null;
17258
17356
  }
17259
17357
  }
17260
17358
 
@@ -33455,8 +33553,19 @@ class Dropdown extends GNCoreInstance {
33455
33553
  });
33456
33554
  if (this.$options.value) {
33457
33555
  if (this.$options.multiple) {
33458
- const values = this.$options.value.split(',');
33459
- this.$options.value = this.$options.flatData.filter((opt) => values.includes(opt.value) && opt.text);
33556
+ // multiple 모드에서 다양한 타입의 value를 문자열 배열로 변환
33557
+ // 지원 타입: 문자열(쉼표 구분), 객체 배열, 단일 객체, 문자열 배열
33558
+ const values = typeof this.$options.value === 'string'
33559
+ ? this.$options.value.split(',') // 케이스 1: 'item1,item2,item3'
33560
+ : Array.isArray(this.$options.value)
33561
+ ? this.$options.value.map((v) => (typeof v === 'object' && v !== null && 'value' in v ? String(v.value) : String(v))) // 케이스 3: [{value:'item1', text:'항목1'}, ...] 또는 ['item1', 'item2']
33562
+ : typeof this.$options.value === 'object' && this.$options.value !== null && 'value' in this.$options.value
33563
+ ? [String(this.$options.value.value)] // 케이스 2: {value:'item1', text:'항목1'}
33564
+ : [String(this.$options.value)]; // 기타: 숫자 등
33565
+ this.$options.value = this.$options.flatData.filter((opt) => {
33566
+ const optValue = typeof opt.value === 'string' ? opt.value : String(opt.value);
33567
+ return values.includes(optValue) && opt.text;
33568
+ });
33460
33569
  }
33461
33570
  else {
33462
33571
  this.$options.value = this.$options.flatData.find((opt) => opt.value + '' === this.$options.value + '' && opt.text);
@@ -36971,26 +37080,8 @@ class DataGrid extends GNCoreInstance {
36971
37080
  }
36972
37081
  this.$event(this, 'onSort', column);
36973
37082
  },
36974
- renderHeader: (columns) => {
36975
- this.$options.hasOrder &&
36976
- !this.$options.readonly &&
36977
- columns.push({
36978
- label: this.$options.textSets.orderLabel,
36979
- key: 'btnOrder',
36980
- style: {
36981
- width: '50px'
36982
- }
36983
- });
36984
- this.$options.hasDelete &&
36985
- !this.$options.readonly &&
36986
- columns.push({
36987
- label: this.$options.textSets.deleteLabel,
36988
- key: 'btnDelete',
36989
- style: {
36990
- width: '30px'
36991
- }
36992
- });
36993
- this._setColumnsTemplate();
37083
+ renderHeader: (columns, isReset = false) => {
37084
+ this._setColumnsTemplate(isReset);
36994
37085
  return (createElement$1("div", { className: "gn-datagrid-header-row", style: {
36995
37086
  'grid-template-columns': this._columnsTemplate.join(' ')
36996
37087
  } },
@@ -37029,6 +37120,10 @@ class DataGrid extends GNCoreInstance {
37029
37120
  column.draggable && (this.$options.headers ? idx < this.$options.headers.length - 1 : true) && createElement$1("span", { className: "is-handle", "data-index": idx })));
37030
37121
  },
37031
37122
  renderBody: (data, columns) => {
37123
+ // 헤더가 숨겨진 경우에도 body 렌더 전에 템플릿 폭을 준비한다
37124
+ if (!this._columnsTemplate || !this._columnsTemplate.length) {
37125
+ this._setColumnsTemplate();
37126
+ }
37032
37127
  rowIdx$1 = 0;
37033
37128
  return (createElement$1("div", { className: "gn-datagrid-body", style: {
37034
37129
  maxHeight: this.$options.bodyHeight ? this.$options.bodyHeight : 'auto'
@@ -37042,7 +37137,6 @@ class DataGrid extends GNCoreInstance {
37042
37137
  });
37043
37138
  },
37044
37139
  renderRow: (row, columns, depth = 0, hasChild, isOpened, isCheck = false) => {
37045
- row._depth = depth;
37046
37140
  const _index = rowIdx$1++;
37047
37141
  if (row.isChecked) {
37048
37142
  isCheck = true;
@@ -37176,8 +37270,9 @@ class DataGrid extends GNCoreInstance {
37176
37270
  e.stopPropagation();
37177
37271
  toggler = parents(e.currentTarget, '.gn-datagrid-body-row');
37178
37272
  }
37179
- const children = nextUntil(toggler, '.gn-datagrid-body-row[data-depth="' + row._depth + '"]').filter((x) => {
37180
- return x.dataset.depth > row._depth;
37273
+ const rowDepth = Number(attr(toggler, 'data-depth')) || 0;
37274
+ const children = nextUntil(toggler, '.gn-datagrid-body-row[data-depth="' + rowDepth + '"]').filter((x) => {
37275
+ return Number(attr(x, 'data-depth')) > rowDepth;
37181
37276
  });
37182
37277
  type = type ? type : hasClass(toggler, 'is-collapsed') ? 'expand' : 'collapse';
37183
37278
  if (type === 'collapse') {
@@ -37193,7 +37288,7 @@ class DataGrid extends GNCoreInstance {
37193
37288
  //show childs
37194
37289
  removeClass(toggler, 'is-collapsed');
37195
37290
  removeClass(children.filter((x) => {
37196
- return x.dataset.depth == row._depth + 1;
37291
+ return Number(attr(x, 'data-depth')) == rowDepth + 1;
37197
37292
  }), 'is-hidden');
37198
37293
  this.$event(this, 'onToggle', 'expanded', row, index$1(toggler));
37199
37294
  }
@@ -37251,13 +37346,15 @@ class DataGrid extends GNCoreInstance {
37251
37346
  e.stopPropagation();
37252
37347
  const checker = parents(e.currentTarget, '.gn-datagrid-body-row');
37253
37348
  const checkerState = e.target.checked;
37254
- find('.is-allChecker', this.$el).checked = false;
37349
+ const allChecker = find('.is-allChecker', this.$el);
37350
+ allChecker && (allChecker.checked = false);
37351
+ const rowDepth = Number(attr(checker, 'data-depth')) || 0;
37255
37352
  // 1. row에 자식노드가 있는지 확인한다.
37256
37353
  if (this.$options.checkCapturing && row[this.$options.childField] && row[this.$options.childField].length) {
37257
37354
  // 2. 자식노드가 있는경우 자식 체크박스도 함께 토글한다.
37258
- nextUntil(checker, '.gn-datagrid-body-row[data-depth="' + row._depth + '"]')
37355
+ nextUntil(checker, '.gn-datagrid-body-row[data-depth="' + rowDepth + '"]')
37259
37356
  .filter((x) => {
37260
- return x.dataset.depth > row._depth;
37357
+ return Number(attr(x, 'data-depth')) > rowDepth;
37261
37358
  })
37262
37359
  .forEach((x) => {
37263
37360
  const _checker = find('.is-rowChecker', x);
@@ -37267,17 +37364,17 @@ class DataGrid extends GNCoreInstance {
37267
37364
  });
37268
37365
  }
37269
37366
  // 3. 체크 해제인 경우만 부모노드가 있는지 확인한다.
37270
- if (this.$options.checkCapturing && row._depth > 0 && !checkerState) {
37367
+ if (this.$options.checkCapturing && rowDepth > 0 && !checkerState) {
37271
37368
  // 4. 부모노드가 체크되어 있는지 확인한다
37272
37369
  const exeDepth = [];
37273
37370
  prevUntil(checker, '.gn-datagrid-body-row[data-depth="0"]')
37274
37371
  .filter((x) => {
37275
- const _thisDepth = x.dataset.depth;
37372
+ const _thisDepth = attr(x, 'data-depth');
37276
37373
  if (exeDepth.includes(_thisDepth)) {
37277
37374
  return false;
37278
37375
  }
37279
37376
  exeDepth.push(_thisDepth);
37280
- return _thisDepth < row._depth;
37377
+ return Number(_thisDepth) < rowDepth;
37281
37378
  })
37282
37379
  .forEach((x) => {
37283
37380
  const _checker = find('.is-rowChecker', x);
@@ -37293,9 +37390,21 @@ class DataGrid extends GNCoreInstance {
37293
37390
  if (hasCheck === undefined) {
37294
37391
  hasCheck = this.$options.hasCheck;
37295
37392
  }
37296
- this.$options.headers = headers;
37393
+ const prevHeaders = [...(this.$options.headers || [])];
37394
+ // _prepareHeaders가 배열을 변경하므로 비교용(prevHeaders)과 가공용(baseHeaders)을 분리한다
37395
+ const baseHeaders = headers ? [...headers] : [...prevHeaders];
37396
+ const preparedHeaders = this._prepareHeaders(baseHeaders);
37397
+ // 헤더 배열의 내용(길이, key)이 바뀐 경우에만 DOM 기반 폭 계산을 리셋한다
37398
+ const isReset = prevHeaders.length !== preparedHeaders.length || prevHeaders.some((header, idx) => { var _a; return header.key !== ((_a = preparedHeaders[idx]) === null || _a === void 0 ? void 0 : _a.key); });
37297
37399
  this.$options.hasCheck = hasCheck;
37298
- this.$template.reRender(find('.gn-datagrid-header-row', this.$el), this._hidden.renderHeader(this.$options.headers));
37400
+ const headerRow = find('.gn-datagrid-header-row', this.$el);
37401
+ // 헤더가 없을 때도 컬럼 수/폭 변경을 반영하기 위해 템플릿을 갱신한다
37402
+ if (this.$options.hasHeader && headerRow) {
37403
+ this.$template.reRender(headerRow, this._hidden.renderHeader(preparedHeaders, isReset));
37404
+ }
37405
+ else {
37406
+ this._setColumnsTemplate(isReset);
37407
+ }
37299
37408
  this._hidden.resetData(data ? arrClone(data) : this.$options.data);
37300
37409
  this.$render(this.$options);
37301
37410
  isFunction(resolve) && resolve();
@@ -37310,6 +37419,10 @@ class DataGrid extends GNCoreInstance {
37310
37419
  });
37311
37420
  },
37312
37421
  awaitData: (data) => {
37422
+ // asyncData 콜백 실행 중 컴포넌트가 destroy된 경우 안전하게 종료
37423
+ if (!this.$options || !this._hidden) {
37424
+ return;
37425
+ }
37313
37426
  if (this.$options.asyncData && this.$options.paginator && !this._paginator) {
37314
37427
  this._paginator = new Pagination('pagination', find('.gn-datagrid-footer', this.$el), {
37315
37428
  total: this.$options.paginator.total || 0,
@@ -37334,7 +37447,8 @@ class DataGrid extends GNCoreInstance {
37334
37447
  this._fixCellStyleOnDraggable();
37335
37448
  // 체크박스가 있는경우 전체 체크항목을 해제해준다
37336
37449
  if (this.$options.hasCheck) {
37337
- find('.is-allChecker', this.$el).checked = false;
37450
+ const allChecker = find('.is-allChecker', this.$el);
37451
+ allChecker && (allChecker.checked = false);
37338
37452
  }
37339
37453
  isFunction(resolve) && resolve();
37340
37454
  });
@@ -37369,8 +37483,18 @@ class DataGrid extends GNCoreInstance {
37369
37483
  stopRowSelectEvent: (e) => {
37370
37484
  e.stopPropagation();
37371
37485
  },
37372
- deleteRow: (index) => {
37373
- this.$options.data = this.$options.data.filter((_data, idx) => index !== idx);
37486
+ deleteRow: async (index) => {
37487
+ var _a;
37488
+ const confirmMessage = (_a = this.$options.textSets) === null || _a === void 0 ? void 0 : _a.deleteConfirmMessage;
37489
+ if (confirmMessage && !window.confirm(confirmMessage)) {
37490
+ return;
37491
+ }
37492
+ const removedData = this._hidden.findData(index);
37493
+ const cancelled = await this.$event(this, 'onDelete', removedData, index);
37494
+ if (cancelled) {
37495
+ return;
37496
+ }
37497
+ this._hidden.deleteData(index);
37374
37498
  this._hidden.resetData(this.$options.data);
37375
37499
  this.$event(this, 'onChange', this.$options.data);
37376
37500
  },
@@ -37405,22 +37529,38 @@ class DataGrid extends GNCoreInstance {
37405
37529
  col.offHover && col.offHover.call(this, row, col, index, e);
37406
37530
  },
37407
37531
  findData: (index) => {
37408
- let deter = 0, indexData = null;
37409
- const findIndex = (datas, index) => {
37410
- return datas.some((data) => {
37411
- if (index === deter) {
37412
- indexData = data;
37532
+ return this._hidden.walkByIndex(index, 'find');
37533
+ },
37534
+ deleteData: (index) => {
37535
+ return this._hidden.walkByIndex(index, 'delete');
37536
+ },
37537
+ walkByIndex: (index, action) => {
37538
+ let deter = 0;
37539
+ let result = null;
37540
+ const findIndex = (datas) => {
37541
+ for (let i = 0; i < datas.length; i++) {
37542
+ if (deter === index) {
37543
+ switch (action) {
37544
+ case 'find':
37545
+ result = datas[i];
37546
+ break;
37547
+ case 'delete':
37548
+ result = datas.splice(i, 1)[0];
37549
+ break;
37550
+ }
37413
37551
  return true;
37414
37552
  }
37415
- ++deter;
37416
- if (isArray$1(data[this.$options.childField]) && data[this.$options.childField].length) {
37417
- return findIndex(data[this.$options.childField], index);
37553
+ deter++;
37554
+ const children = datas[i][this.$options.childField];
37555
+ if (Array.isArray(children) && children.length) {
37556
+ if (findIndex(children))
37557
+ return true;
37418
37558
  }
37419
- return false;
37420
- });
37559
+ }
37560
+ return false;
37421
37561
  };
37422
- findIndex(this.$options.data, index);
37423
- return indexData;
37562
+ findIndex(this.$options.data);
37563
+ return result;
37424
37564
  },
37425
37565
  getChecked: () => {
37426
37566
  return findAll('.is-rowChecker', this.$el)
@@ -37507,11 +37647,13 @@ class DataGrid extends GNCoreInstance {
37507
37647
  hasOrder: false,
37508
37648
  hasDelete: false,
37509
37649
  isEllipsis: false,
37650
+ hasHeader: true,
37510
37651
  data: [],
37511
37652
  textSets: {
37512
37653
  noData: 'No records available.',
37513
37654
  orderLabel: '',
37514
- deleteLabel: ''
37655
+ deleteLabel: '',
37656
+ deleteConfirmMessage: ''
37515
37657
  },
37516
37658
  childField: 'child',
37517
37659
  checkCapturing: true,
@@ -37529,7 +37671,8 @@ class DataGrid extends GNCoreInstance {
37529
37671
  onCheck: true,
37530
37672
  onDoubleClick: true,
37531
37673
  onChange: true,
37532
- onDragEnd: true
37674
+ onDragEnd: true,
37675
+ onDelete: true
37533
37676
  };
37534
37677
  this.methods = {
37535
37678
  reRender(options) {
@@ -37589,18 +37732,21 @@ class DataGrid extends GNCoreInstance {
37589
37732
  this.$selector = this.$selector;
37590
37733
  this.$init(this, options);
37591
37734
  }
37592
- _setColumnsTemplate() {
37735
+ _setColumnsTemplate(isReset = false) {
37593
37736
  // header cell의 각 넓이를 배열로 가져온다
37594
37737
  // ! 모든 컬럼의 넓이가 지정된 경우 이동(btnOrder), 삭제(btnDelete) 컬럼을 제외한 마지막 컬럼은 1fr로 고정
37595
37738
  const _isfixedAllWidth = this.$options.headers.every((header) => { var _a; return ((_a = header.style) === null || _a === void 0 ? void 0 : _a.width) !== undefined; });
37596
37739
  const _fixedTemplateColumn = this.$options.headers.findLast((header) => !this._isSystemAddedColumn(header.key) && !header.isHidden);
37740
+ // 헤더 DOM이 없거나 모든 일반 컬럼이 숨겨진 경우에도 안전하게 비교할 수 있도록 key만 옵셔널하게 캐싱한다
37741
+ const _fixedTemplateKey = _fixedTemplateColumn === null || _fixedTemplateColumn === void 0 ? void 0 : _fixedTemplateColumn.key;
37597
37742
  const columns = findAll('.gn-datagrid-header-cell', this.$el);
37598
- if (this.$el && columns.length) {
37743
+ // isReset이면 기존 DOM 폭을 재사용하지 않고 헤더 정의로 재계산
37744
+ if (this.$el && columns.length && !isReset) {
37599
37745
  this._columnsTemplate = findAll('.gn-datagrid-header-cell', this.$el).map((header, idx) => {
37600
37746
  if (this.$options.headers[idx].isHidden) {
37601
37747
  return '';
37602
37748
  }
37603
- else if (_isfixedAllWidth && this.$options.headers[idx].key === _fixedTemplateColumn.key) {
37749
+ else if (_isfixedAllWidth && _fixedTemplateKey && this.$options.headers[idx].key === _fixedTemplateKey) {
37604
37750
  return '1fr';
37605
37751
  }
37606
37752
  else {
@@ -37614,7 +37760,7 @@ class DataGrid extends GNCoreInstance {
37614
37760
  if (header.isHidden) {
37615
37761
  return '';
37616
37762
  }
37617
- else if (_isfixedAllWidth && header.key === _fixedTemplateColumn.key) {
37763
+ else if (_isfixedAllWidth && _fixedTemplateKey && header.key === _fixedTemplateKey) {
37618
37764
  return '1fr';
37619
37765
  }
37620
37766
  else {
@@ -37635,18 +37781,45 @@ class DataGrid extends GNCoreInstance {
37635
37781
  _isSystemAddedColumn(key) {
37636
37782
  return ['btnOrder', 'btnDelete'].includes(key);
37637
37783
  }
37784
+ // 옵션에 따른 추가 해더 구성
37785
+ _prepareHeaders(headers = []) {
37786
+ const hasOrderColumn = headers.some((header) => header.key === 'btnOrder');
37787
+ const hasDeleteColumn = headers.some((header) => header.key === 'btnDelete');
37788
+ if (this.$options.hasOrder && !this.$options.readonly && !hasOrderColumn) {
37789
+ headers.push({
37790
+ label: this.$options.textSets.orderLabel,
37791
+ key: 'btnOrder',
37792
+ style: {
37793
+ width: '50px'
37794
+ }
37795
+ });
37796
+ }
37797
+ if (this.$options.hasDelete && !this.$options.readonly && !hasDeleteColumn) {
37798
+ headers.push({
37799
+ label: this.$options.textSets.deleteLabel,
37800
+ key: 'btnDelete',
37801
+ style: {
37802
+ width: '30px'
37803
+ }
37804
+ });
37805
+ }
37806
+ this.$options.headers = headers;
37807
+ return headers;
37808
+ }
37638
37809
  template(config) {
37639
37810
  const styles = {};
37811
+ const headers = this._prepareHeaders(config.headers);
37640
37812
  return (createElement$1("div", { id: this._uid, className: 'gn-datagrid' +
37641
37813
  (config.style ? ' is-' + config.style : '') +
37642
37814
  (config.isEllipsis ? ' is-ellipsis' : '') +
37643
37815
  (config.bodyHeight ? ' has-fixed-body' : '') +
37644
37816
  (config.fixHeader ? ' has-fixed-header' : '') +
37645
37817
  (config.fixFooter ? ' has-fixed-footer' : '') +
37818
+ (!config.hasHeader ? ' is-headless' : '') +
37646
37819
  (config.data.some((d) => isArray$1(d[this.$options.childField])) ? ' has-left-padding' : '') +
37647
37820
  (config.disabled ? ' is-disabled' : ''), style: styles },
37648
- createElement$1("div", { className: "gn-datagrid-header" }, this._hidden.renderHeader(config.headers)),
37649
- createElement$1("div", { className: "gn-datagrid-contents", style: { marginTop: this.$options.bodyTopMargin ? this.$options.bodyTopMargin : '0', marginBottom: this.$options.bodyBottomMargin ? this.$options.bodyBottomMargin : '0' } }, this._hidden.renderBody(arrClone(config.data), config.headers)),
37821
+ config.hasHeader && createElement$1("div", { className: "gn-datagrid-header" }, this._hidden.renderHeader(headers)),
37822
+ createElement$1("div", { className: "gn-datagrid-contents", style: { marginTop: this.$options.bodyTopMargin ? this.$options.bodyTopMargin : '0', marginBottom: this.$options.bodyBottomMargin ? this.$options.bodyBottomMargin : '0' } }, this._hidden.renderBody(arrClone(config.data), headers)),
37650
37823
  config.paginator /* 페이지네이터 옵션 확인 */ && createElement$1("div", { className: "gn-datagrid-footer" })));
37651
37824
  }
37652
37825
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -37663,18 +37836,20 @@ class DataGrid extends GNCoreInstance {
37663
37836
  }
37664
37837
  }
37665
37838
  completed() {
37666
- if (this.$options.fixHeader) {
37839
+ if (this.$options.fixHeader && this.$options.hasHeader) {
37667
37840
  const body = find('.gn-datagrid-contents', this.$el);
37668
37841
  const header = find('.gn-datagrid-header', this.$el);
37669
- const _offset = offset(header);
37670
- this.$options.bodyTopMargin = _offset.height ? _offset.height - 1 + 'px' : '2.4rem';
37671
- css$1(body, 'margin-top', this.$options.bodyTopMargin);
37842
+ if (header) {
37843
+ const _offset = offset(header);
37844
+ this.$options.bodyTopMargin = _offset.height ? _offset.height - 1 + 'px' : '2.4rem';
37845
+ css$1(body, 'margin-top', this.$options.bodyTopMargin);
37846
+ }
37672
37847
  if (this.$options.paginator) {
37673
37848
  this.$options.bodyBottomMargin = '2.4rem';
37674
37849
  css$1(body, 'margin-bottom', this.$options.bodyBottomMargin);
37675
37850
  }
37676
37851
  }
37677
- if (this.$options.fixHeader || this.$options.bodyHeight) {
37852
+ if ((this.$options.fixHeader && this.$options.hasHeader) || this.$options.bodyHeight) {
37678
37853
  this._hidden.setBlankHeader();
37679
37854
  on(window, 'resize', this._hidden.setBlankHeader);
37680
37855
  }
@@ -39019,7 +39194,9 @@ class MenuButton extends GNCoreInstance {
39019
39194
  super(name, selector, options);
39020
39195
  this._hidden = {
39021
39196
  open: () => {
39022
- addClass(this.$el, 'is-open');
39197
+ if (!this.$options.disabled) {
39198
+ addClass(this.$el, 'is-open');
39199
+ }
39023
39200
  },
39024
39201
  close: () => {
39025
39202
  removeClass(this.$el, 'is-open');
@@ -39031,6 +39208,62 @@ class MenuButton extends GNCoreInstance {
39031
39208
  changeText: (buttonText) => {
39032
39209
  this.$options.textSets.buttonText = buttonText;
39033
39210
  html(find('.menuButton-text', this.$el), buttonText);
39211
+ },
39212
+ disable: () => {
39213
+ this.$options.disabled = true;
39214
+ const buttonEl = find('button', this.$el);
39215
+ if (buttonEl) {
39216
+ attr(buttonEl, 'disabled', true);
39217
+ }
39218
+ addClass(this.$el, 'is-disabled');
39219
+ this._hidden.close();
39220
+ },
39221
+ enable: () => {
39222
+ this.$options.disabled = false;
39223
+ const buttonEl = find('button', this.$el);
39224
+ if (buttonEl) {
39225
+ removeAttr(buttonEl, 'disabled');
39226
+ }
39227
+ removeClass(this.$el, 'is-disabled');
39228
+ },
39229
+ renderMenus: (menus, depth = 0, parentPath = '') => {
39230
+ return (createElement$1("ul", { className: depth > 0 ? 'menuButton-submenu' : '' }, menus.map((menu, index) => {
39231
+ const hasChild = menu.child && isArray$1(menu.child) && menu.child.length > 0;
39232
+ const hasHtml = !!menu.html;
39233
+ // html이 있으면 innerHTML이 모든 자식 요소를 덮어쓰므로 서브메뉴를 렌더링하지 않음
39234
+ const canRenderChild = hasChild && depth < 2 && !hasHtml; // 최대 2단계까지만 허용
39235
+ const isDisabled = menu.disabled === true;
39236
+ const isActived = menu.actived === true;
39237
+ // 부모 경로를 포함한 고유한 ID 생성
39238
+ const currentPath = parentPath ? `${parentPath}-${index}` : `${index}`;
39239
+ const uniqueId = `${this._uid}-${currentPath}`;
39240
+ return (createElement$1("li", { id: uniqueId, className: 'menuButton-menu' +
39241
+ (this.$options.align ? ' has-text-' + this.$options.align : '') +
39242
+ (canRenderChild ? ' has-submenu' : '') +
39243
+ (depth > 0 ? ' is-submenu-item' : '') +
39244
+ (isDisabled ? ' is-disabled' : '') +
39245
+ (isActived ? ' is-actived' : ''), "on-click": (e) => {
39246
+ // disabled 상태이거나 자식 메뉴가 있는 경우 클릭 이벤트 처리하지 않음
39247
+ if (isDisabled) {
39248
+ e.stopPropagation();
39249
+ e.preventDefault();
39250
+ return;
39251
+ }
39252
+ // 자식 메뉴가 없는 경우에만 select 이벤트 발생
39253
+ if (!canRenderChild) {
39254
+ e.stopPropagation();
39255
+ this._hidden.select.call(this, menu, e);
39256
+ }
39257
+ }, innerHTML: hasHtml ? menu.html : '' },
39258
+ hasHtml ? ('') : (createElement$1("span", { className: "menuButton-menu-content" },
39259
+ createElement$1("span", { className: "menuButton-menu-text" }, menu.text),
39260
+ canRenderChild && (createElement$1("span", { className: "menuButton-menu-arrow" },
39261
+ createElement$1("i", { className: "fas fa-caret-right" }))))),
39262
+ canRenderChild && this._hidden.renderMenus.call(this, menu.child, depth + 1, currentPath)));
39263
+ })));
39264
+ },
39265
+ renderSub: (data) => {
39266
+ return createElement$1("div", null, isArray$1(data) && data.length && isArray$1(data[0]) ? data.map((menus) => this._hidden.renderMenus.call(this, menus)) : this._hidden.renderMenus.call(this, data));
39034
39267
  }
39035
39268
  };
39036
39269
  this.config = {
@@ -39050,6 +39283,19 @@ class MenuButton extends GNCoreInstance {
39050
39283
  },
39051
39284
  buttonText(text) {
39052
39285
  this._hidden.changeText(text);
39286
+ },
39287
+ reRender(data) {
39288
+ this.$options.data = data;
39289
+ const menuMenusEl = find('.menuButton-menus > div', this.$el);
39290
+ if (menuMenusEl && this.$template) {
39291
+ this.$template.reRender(menuMenusEl, this._hidden.renderSub.call(this, data));
39292
+ }
39293
+ },
39294
+ disabled() {
39295
+ this._hidden.disable();
39296
+ },
39297
+ enabled() {
39298
+ this._hidden.enable();
39053
39299
  }
39054
39300
  };
39055
39301
  this.$selector = this.$selector;
@@ -39060,23 +39306,19 @@ class MenuButton extends GNCoreInstance {
39060
39306
  if (config.width) {
39061
39307
  styles.width = getUnit('width', config.width);
39062
39308
  }
39063
- const renderMenus = (menus) => {
39064
- return (createElement$1("ul", null, menus.map((menu, index) => (createElement$1("li", { id: this._uid + '-' + index, className: 'menuButton-menu' + (config.align ? ' has-text-' + config.align : ''), "on-click": (e) => {
39065
- this._hidden.select.call(this, menu, e);
39066
- }, innerHTML: menu.html ? menu.html : '' }, menu.html ? '' : menu.text)))));
39067
- };
39068
- const renderSub = (data) => {
39069
- return createElement$1("div", null, isArray$1(data) && data.length && isArray$1(data[0]) ? data.map((menus) => renderMenus(menus)) : renderMenus(data));
39070
- };
39071
- return (createElement$1("div", { id: this._uid, className: 'gn-menuButton' + (config.color ? ' is-' + config.color : '') + (config.style ? ' is-' + config.style : '') + (config.size ? ' is-' + config.size : ''), style: styles },
39072
- createElement$1("button", { type: "button", className: config.align ? 'has-text-' + config.align : '', "on-click": this._hidden.open },
39309
+ return (createElement$1("div", { id: this._uid, className: 'gn-menuButton' +
39310
+ (config.color ? ' is-' + config.color : '') +
39311
+ (config.style ? ' is-' + config.style : '') +
39312
+ (config.size ? ' is-' + config.size : '') +
39313
+ (config.disabled ? ' is-disabled' : ''), style: styles },
39314
+ createElement$1("button", { type: "button", className: config.align ? 'has-text-' + config.align : '', disabled: config.disabled, "on-click": this._hidden.open },
39073
39315
  config.icon && (createElement$1("span", { className: 'gn-icon is-' + (config.size === 'large' ? 'medium' : config.size === 'medium' ? 'normal' : 'small') },
39074
39316
  createElement$1("i", { className: 'fas fa-' + config.icon }),
39075
39317
  ' ')),
39076
39318
  createElement$1("span", { className: "gn-icon is-small menuButton-icon" },
39077
39319
  createElement$1("i", { className: "fas fa-caret-down" })),
39078
39320
  createElement$1("span", { className: "menuButton-text" }, config.textSets.buttonText)),
39079
- createElement$1("div", { className: "menuButton-menus" }, renderSub(config.data))));
39321
+ createElement$1("div", { className: "menuButton-menus" }, this._hidden.renderSub.call(this, config.data))));
39080
39322
  }
39081
39323
  completed() {
39082
39324
  // 해당 컴포넌트 외 클릭 시 menu panel 숨김
@@ -39287,6 +39529,7 @@ class MultiTextArea extends GNCoreInstance {
39287
39529
  }
39288
39530
  }
39289
39531
 
39532
+ library$1.add(icons$1, icons);
39290
39533
  class Picklist extends GNCoreInstance {
39291
39534
  constructor(name, selector, options = {}) {
39292
39535
  super(name, selector, options);
@@ -39428,17 +39671,26 @@ class Picklist extends GNCoreInstance {
39428
39671
  },
39429
39672
  renderSub: (item) => {
39430
39673
  const items = this.$options.data[item] || [];
39431
- return (createElement$1("ul", null, items.map((option, index) => (createElement$1("li", { id: this._uid + '_opt_' + index, className: 'dropdown-item' + (option.selected ? ' is-active' : ''), "data-value": option.value, "on-click": this._hidden.toggle.bind(this), "on-dblclick": this._hidden.move.bind(this, item === 'source' ? 'add' : 'remove', [
39432
- {
39433
- value: option.value,
39434
- text: option.text
39435
- }
39436
- ]) },
39437
- createElement$1("span", { className: "dropdown-text" }, option.text))))));
39674
+ return (createElement$1("ul", null, items.map((option, index) => {
39675
+ var _a;
39676
+ return (createElement$1("li", { id: this._uid + '_opt_' + index, className: 'dropdown-item' + (option.selected ? ' is-active' : ''), "data-value": option.value, "on-click": !option.text ? null : this._hidden.toggle.bind(this), "on-dblclick": !option.text
39677
+ ? null
39678
+ : this._hidden.move.bind(this, item === 'source' ? 'add' : 'remove', [
39679
+ {
39680
+ value: option.value,
39681
+ text: option.text,
39682
+ html: (_a = option.html) !== null && _a !== void 0 ? _a : null
39683
+ }
39684
+ ]) },
39685
+ createElement$1("span", { className: "dropdown-text", innerHTML: option.html ? option.html : '' }, option.html ? ('') : option.icon ? (createElement$1("span", null,
39686
+ createElement$1("span", { className: 'gn-icon' + (this.$options.size ? ' is-' + this.$options.size : '') },
39687
+ createElement$1("i", { className: (this.isBrandIcon(option.icon) ? 'fab' : 'fa') + ` fa-${option.icon}` })),
39688
+ escapeEntity(option.text))) : (escapeEntity(option.text)))));
39689
+ })));
39438
39690
  },
39439
39691
  getSelection: (target) => {
39440
39692
  return findAll('.is-active', this.$options.delegates[target]).map((select) => {
39441
- return { value: attr(select, 'data-value'), text: text$1(select) };
39693
+ return this.$options.data[target].find((option) => option.value === attr(select, 'data-value'));
39442
39694
  });
39443
39695
  },
39444
39696
  disable: () => {
@@ -39598,6 +39850,11 @@ class Picklist extends GNCoreInstance {
39598
39850
  style(find('.picklist-target .dropdown-items', this.$el), 'height', getUnit('height', getNumber(this.$options.height) - getNumber(style(find('.picklist-target .picklist-caption', this.$el), 'height'))));
39599
39851
  }
39600
39852
  }
39853
+ isBrandIcon(iconName) {
39854
+ const iconLookup = { prefix: 'fab', iconName: iconName };
39855
+ const iconDefinition = findIconDefinition$1(iconLookup);
39856
+ return iconDefinition !== undefined;
39857
+ }
39601
39858
  }
39602
39859
 
39603
39860
  class Progressbar extends GNCoreInstance {
@@ -39756,6 +40013,409 @@ class SelectButton extends GNCoreInstance {
39756
40013
  }
39757
40014
  }
39758
40015
 
40016
+ const CLASS_DRAGGING = 'is-dragging';
40017
+ const CLASS_GROUP_DRAGGING = 'is-group-dragging';
40018
+ const CLASS_DRAG_OVER_TOP = 'is-drag-over-top';
40019
+ const CLASS_DRAG_OVER_BOTTOM = 'is-drag-over-bottom';
40020
+ const DEFAULT_NO_DATA = 'No records available.';
40021
+ const DRAG_STATE_CLASSES = [CLASS_DRAGGING, CLASS_GROUP_DRAGGING, CLASS_DRAG_OVER_TOP, CLASS_DRAG_OVER_BOTTOM];
40022
+ class SortableList extends GNCoreInstance {
40023
+ constructor(name, selector, options = {}) {
40024
+ super(name, selector, options);
40025
+ this._dragging = null;
40026
+ this._selection = new Set();
40027
+ this.DRAG_IMAGE_OFFSET_X = 0;
40028
+ this.DRAG_IMAGE_OFFSET_Y = 0;
40029
+ this.DRAG_OVER_THRESHOLD_RATIO = 0.5;
40030
+ this._hidden = {
40031
+ toggle: (e) => {
40032
+ if (this.$options.disabled) {
40033
+ return;
40034
+ }
40035
+ const value = attr(e.currentTarget, 'data-value');
40036
+ const hasItem = this.$options.data.some((option) => option.value === value);
40037
+ if (!value || !hasItem) {
40038
+ return;
40039
+ }
40040
+ if (this._selection.has(value)) {
40041
+ this._selection.delete(value);
40042
+ removeClass(e.currentTarget, 'is-active');
40043
+ }
40044
+ else {
40045
+ this._selection.add(value);
40046
+ addClass(e.currentTarget, 'is-active');
40047
+ }
40048
+ },
40049
+ sort: (dir) => {
40050
+ const items = this.$options.data || [];
40051
+ const selected = this._hidden.getSelection();
40052
+ if (!selected.length || selected.length === items.length) {
40053
+ return;
40054
+ }
40055
+ const selectedValues = new Set(selected.map((item) => item.value));
40056
+ // dir: up/down → 이동, up-all/down-all → 맨 위/맨 아래로 보내기
40057
+ if (dir.indexOf('all') > -1) {
40058
+ this.$options.data = items.slice().sort((a, b) => {
40059
+ const _sort = dir === 'up-all' ? -1 : 1;
40060
+ const aSel = selectedValues.has(a.value);
40061
+ const bSel = selectedValues.has(b.value);
40062
+ if (aSel && bSel) {
40063
+ return 0;
40064
+ }
40065
+ else if (aSel) {
40066
+ return _sort;
40067
+ }
40068
+ else if (bSel) {
40069
+ return _sort * -1;
40070
+ }
40071
+ return 0;
40072
+ });
40073
+ }
40074
+ else {
40075
+ if (dir === 'up') {
40076
+ const reordered = items.slice();
40077
+ // 한 번에 한 칸씩만 올려 상대 순서를 유지한다
40078
+ for (let i = 1; i < reordered.length; i++) {
40079
+ if (!selectedValues.has(reordered[i].value) || selectedValues.has(reordered[i - 1].value)) {
40080
+ continue;
40081
+ }
40082
+ const temp = reordered[i - 1];
40083
+ reordered[i - 1] = reordered[i];
40084
+ reordered[i] = temp;
40085
+ }
40086
+ this.$options.data = reordered;
40087
+ }
40088
+ else {
40089
+ let reordered = [];
40090
+ let itemsToMoveDown = [];
40091
+ items.forEach((option) => {
40092
+ // reordered: 최종 순서를 쌓는 버퍼, itemsToMoveDown: 아래로 밀어야 할 선택 항목 임시 저장
40093
+ // 비선택 항목은 흐름대로 push, 선택 항목은 dir에 따라 위/아래로 밀어 넣음
40094
+ if (!selectedValues.has(option.value)) {
40095
+ reordered.push(option);
40096
+ if (itemsToMoveDown.length) {
40097
+ reordered = reordered.concat(itemsToMoveDown);
40098
+ itemsToMoveDown = [];
40099
+ }
40100
+ }
40101
+ else if (dir === 'down') {
40102
+ itemsToMoveDown.push(option);
40103
+ }
40104
+ });
40105
+ if (itemsToMoveDown.length) {
40106
+ reordered = reordered.concat(itemsToMoveDown);
40107
+ itemsToMoveDown = [];
40108
+ }
40109
+ this.$options.data = reordered.slice();
40110
+ }
40111
+ }
40112
+ this._hidden.reRender();
40113
+ this._hidden.updateControls();
40114
+ this.$event(this, 'onChange', this.$options.data);
40115
+ },
40116
+ renderSub: () => {
40117
+ var _a, _b;
40118
+ const items = this.$options.data || [];
40119
+ const hasCols = items.some((item) => isArray$1(item.cols) && item.cols.length);
40120
+ if (!items.length) {
40121
+ return (createElement$1("ul", { className: "sortablelist-rows" },
40122
+ createElement$1("li", { className: "dropdown-item is-empty" }, (_b = (_a = this.$options.textSets) === null || _a === void 0 ? void 0 : _a.noData) !== null && _b !== void 0 ? _b : DEFAULT_NO_DATA)));
40123
+ }
40124
+ return (createElement$1("ul", { className: 'sortablelist-rows' + (hasCols ? ' is-cols' : '') }, items.map((option, index) => {
40125
+ var _a;
40126
+ return (createElement$1("li", { id: this._uid + '_opt_' + index, className: 'dropdown-item' + (this._selection.has(option.value) ? ' is-active' : ''), "data-value": option.value, draggable: this.$options.draggable && !this.$options.disabled ? true : null, "on-click": this._hidden.toggle.bind(this), "on-dragstart": this.$options.draggable ? this._hidden.dragStart.bind(this) : null, "on-dragover": this.$options.draggable ? this._hidden.dragOver.bind(this) : null, "on-dragleave": this.$options.draggable ? this._hidden.dragLeave.bind(this) : null, "on-drop": this.$options.draggable ? this._hidden.drop.bind(this) : null, "on-dragend": this.$options.draggable ? this._hidden.dragEnd.bind(this) : null }, hasCols && isArray$1(option.cols) && option.cols.length ? (createElement$1("div", { className: "sortablelist-cols" }, option.cols.map((col, colIndex) => (createElement$1("span", { className: "sortablelist-col", "data-col": colIndex }, escapeEntity(col)))))) : (createElement$1("span", { className: "dropdown-text" }, escapeEntity((_a = option.text) !== null && _a !== void 0 ? _a : option.value)))));
40127
+ })));
40128
+ },
40129
+ getSelection: () => {
40130
+ return this.$options.data.filter((option) => this._selection.has(option.value));
40131
+ },
40132
+ reRender: () => {
40133
+ const listContainer = find('ul', this.$options.delegates.list);
40134
+ listContainer && this.$template.reRender(listContainer, this._hidden.renderSub());
40135
+ },
40136
+ syncSelection: () => {
40137
+ const values = new Set(this.$options.data.map((item) => item.value));
40138
+ this._selection.forEach(val => {
40139
+ if (!values.has(val)) {
40140
+ this._selection.delete(val);
40141
+ }
40142
+ });
40143
+ },
40144
+ hydrateSelection: (items) => {
40145
+ this._selection.clear();
40146
+ items.forEach((item) => {
40147
+ if (item.selected) {
40148
+ this._selection.add(item.value);
40149
+ }
40150
+ });
40151
+ },
40152
+ validateData: (items) => {
40153
+ if (!isArray$1(items)) {
40154
+ throw new TypeError('Invalid SortableList data: data must be an array');
40155
+ }
40156
+ const seen = new Set();
40157
+ items.forEach((item) => {
40158
+ if (!item || typeof item.value !== 'string') {
40159
+ throw new TypeError('Invalid SortableList data: value must be string');
40160
+ }
40161
+ if (item.value === '') {
40162
+ throw new TypeError('Invalid SortableList data: value cannot be empty');
40163
+ }
40164
+ if (seen.has(item.value)) {
40165
+ throw new TypeError(`Invalid SortableList data: duplicate value '${item.value}'`);
40166
+ }
40167
+ seen.add(item.value);
40168
+ });
40169
+ },
40170
+ updateControls: () => {
40171
+ const hasData = (this.$options.data || []).length > 0;
40172
+ const disableButtons = this.$options.disabled || !hasData;
40173
+ if (disableButtons) {
40174
+ attr(findAll('button', this.$el), 'disabled', true);
40175
+ }
40176
+ else {
40177
+ removeAttr(findAll('button', this.$el), 'disabled');
40178
+ }
40179
+ if (!hasData) {
40180
+ addClass(this.$el, 'is-empty');
40181
+ }
40182
+ else {
40183
+ removeClass(this.$el, 'is-empty');
40184
+ }
40185
+ },
40186
+ clearDragState: () => {
40187
+ var _a;
40188
+ DRAG_STATE_CLASSES.forEach(className => {
40189
+ removeClass(findAll(`.${className}`, this.$el), className);
40190
+ });
40191
+ if ((_a = this._dragging) === null || _a === void 0 ? void 0 : _a.preview) {
40192
+ this._dragging.preview.remove();
40193
+ }
40194
+ this._dragging = null;
40195
+ },
40196
+ dragStart: (e) => {
40197
+ if (this.$options.disabled || !this.$options.draggable) {
40198
+ return;
40199
+ }
40200
+ // 기존 드래그 상태 정리 (이전 드래그가 중단된 경우 대비)
40201
+ this._hidden.clearDragState();
40202
+ const value = attr(e.currentTarget, 'data-value');
40203
+ if (!value) {
40204
+ return;
40205
+ }
40206
+ const selected = this._hidden.getSelection().map((item) => item.value);
40207
+ const isGroup = selected.length > 1 && selected.includes(value);
40208
+ const dragValues = isGroup ? selected : [value];
40209
+ this._dragging = { values: dragValues, isGroup: isGroup, preview: null };
40210
+ if (e.dataTransfer) {
40211
+ e.dataTransfer.setData('text/plain', value);
40212
+ e.dataTransfer.effectAllowed = 'move';
40213
+ if (isGroup) {
40214
+ const preview = document.createElement('div');
40215
+ preview.className = 'sortablelist-drag-preview';
40216
+ preview.setAttribute('data-count', String(dragValues.length));
40217
+ const list = document.createElement('div');
40218
+ list.className = 'sortablelist-drag-preview-list';
40219
+ dragValues.forEach((dragValue) => {
40220
+ const itemEl = find(`[data-value="${CSS.escape(dragValue)}"]`, this.$options.delegates.list);
40221
+ if (!(itemEl instanceof HTMLElement)) {
40222
+ return;
40223
+ }
40224
+ const clone = itemEl.cloneNode(true);
40225
+ removeClass(clone, CLASS_DRAGGING);
40226
+ removeClass(clone, CLASS_GROUP_DRAGGING);
40227
+ removeClass(clone, CLASS_DRAG_OVER_TOP);
40228
+ removeClass(clone, CLASS_DRAG_OVER_BOTTOM);
40229
+ addClass(clone, 'is-ghost');
40230
+ list.appendChild(clone);
40231
+ });
40232
+ preview.appendChild(list);
40233
+ document.body.appendChild(preview);
40234
+ e.dataTransfer.setDragImage(preview, this.DRAG_IMAGE_OFFSET_X, this.DRAG_IMAGE_OFFSET_Y);
40235
+ this._dragging.preview = preview;
40236
+ }
40237
+ }
40238
+ findAll('.dropdown-item', this.$options.delegates.list).forEach((node) => {
40239
+ const itemValue = attr(node, 'data-value');
40240
+ if (dragValues.includes(itemValue)) {
40241
+ addClass(node, CLASS_DRAGGING);
40242
+ if (isGroup) {
40243
+ addClass(node, CLASS_GROUP_DRAGGING);
40244
+ }
40245
+ }
40246
+ });
40247
+ },
40248
+ dragOver: (e) => {
40249
+ if (!this._dragging || this.$options.disabled || !this.$options.draggable) {
40250
+ return;
40251
+ }
40252
+ e.preventDefault();
40253
+ if (!(e.currentTarget instanceof HTMLElement)) {
40254
+ return;
40255
+ }
40256
+ const target = e.currentTarget;
40257
+ const value = attr(target, 'data-value');
40258
+ if (this._dragging.values.includes(value)) {
40259
+ return;
40260
+ }
40261
+ removeClass(target, CLASS_DRAG_OVER_TOP);
40262
+ removeClass(target, CLASS_DRAG_OVER_BOTTOM);
40263
+ const rect = target.getBoundingClientRect();
40264
+ const midpoint = rect.top + rect.height * this.DRAG_OVER_THRESHOLD_RATIO;
40265
+ if (e.clientY > midpoint) {
40266
+ addClass(target, CLASS_DRAG_OVER_BOTTOM);
40267
+ }
40268
+ else {
40269
+ addClass(target, CLASS_DRAG_OVER_TOP);
40270
+ }
40271
+ },
40272
+ dragLeave: (e) => {
40273
+ if (e.currentTarget instanceof HTMLElement) {
40274
+ removeClass(e.currentTarget, 'is-drag-over-top');
40275
+ removeClass(e.currentTarget, 'is-drag-over-bottom');
40276
+ }
40277
+ },
40278
+ drop: (e) => {
40279
+ if (!this._dragging || this.$options.disabled || !this.$options.draggable) {
40280
+ return;
40281
+ }
40282
+ e.preventDefault();
40283
+ if (!(e.currentTarget instanceof HTMLElement)) {
40284
+ return;
40285
+ }
40286
+ const targetEl = e.currentTarget;
40287
+ const targetValue = attr(targetEl, 'data-value');
40288
+ const dragValues = this._dragging.values;
40289
+ if (!targetValue || dragValues.includes(targetValue)) {
40290
+ this._hidden.clearDragState();
40291
+ return;
40292
+ }
40293
+ const items = this.$options.data;
40294
+ const dragItems = items.filter((item) => dragValues.includes(item.value));
40295
+ const remain = items.filter((item) => !dragValues.includes(item.value));
40296
+ const targetIndex = remain.findIndex((item) => item.value === targetValue);
40297
+ const rect = targetEl.getBoundingClientRect();
40298
+ const midpoint = rect.top + rect.height * this.DRAG_OVER_THRESHOLD_RATIO;
40299
+ const insertAfter = e.clientY > midpoint;
40300
+ const insertIndex = targetIndex === -1 ? remain.length : insertAfter ? targetIndex + 1 : targetIndex;
40301
+ remain.splice(insertIndex, 0, ...dragItems);
40302
+ this.$options.data = remain;
40303
+ this._hidden.reRender();
40304
+ this._hidden.updateControls();
40305
+ this.$event(this, 'onChange', this.$options.data);
40306
+ this._hidden.clearDragState();
40307
+ },
40308
+ dragEnd: () => {
40309
+ this._hidden.clearDragState();
40310
+ },
40311
+ setData: (data) => {
40312
+ // 드래그 중일 경우 상태 정리 (DOM 요소가 제거되면 dragend 이벤트가 발생하지 않을 수 있음)
40313
+ this._hidden.clearDragState();
40314
+ this._hidden.validateData(data);
40315
+ this.$options.data = data;
40316
+ this._hidden.hydrateSelection(data);
40317
+ this._hidden.syncSelection();
40318
+ this._hidden.reRender();
40319
+ this._hidden.updateControls();
40320
+ this.$event(this, 'onChange', this.$options.data);
40321
+ },
40322
+ disable: () => {
40323
+ // 드래그 중일 경우 상태 정리
40324
+ this._hidden.clearDragState();
40325
+ this.$options.disabled = true;
40326
+ addClass(this.$el, 'is-disabled');
40327
+ this._hidden.reRender();
40328
+ this._hidden.updateControls();
40329
+ },
40330
+ enable: () => {
40331
+ // 드래그 중일 경우 상태 정리
40332
+ this._hidden.clearDragState();
40333
+ this.$options.disabled = false;
40334
+ removeClass(this.$el, 'is-disabled');
40335
+ this._hidden.reRender();
40336
+ this._hidden.updateControls();
40337
+ }
40338
+ };
40339
+ this.config = {
40340
+ name: this.$selector.name || this._uid,
40341
+ data: [],
40342
+ delegates: {
40343
+ list: '.sortablelist-items'
40344
+ },
40345
+ buttonPosition: 'left',
40346
+ draggable: false,
40347
+ textSets: {
40348
+ noData: DEFAULT_NO_DATA
40349
+ },
40350
+ height: 150
40351
+ };
40352
+ this.events = {
40353
+ onChange: true
40354
+ };
40355
+ this.methods = {
40356
+ getData() {
40357
+ return this.$options.data;
40358
+ },
40359
+ setData(data) {
40360
+ const next = isArray$1(data) ? data : data && 'data' in data ? data.data : null;
40361
+ if (!isArray$1(next)) {
40362
+ throw new TypeError('Invalid SortableList data: data must be an array');
40363
+ }
40364
+ this._hidden.setData(next);
40365
+ },
40366
+ disable() {
40367
+ this._hidden.disable();
40368
+ },
40369
+ enable() {
40370
+ this._hidden.enable();
40371
+ }
40372
+ };
40373
+ this.$selector = this.$selector;
40374
+ this.$init(this, options);
40375
+ }
40376
+ template(config) {
40377
+ const styles = {};
40378
+ if (config.width) {
40379
+ styles.width = getUnit('width', config.width);
40380
+ }
40381
+ const listStyles = {};
40382
+ if (config.height) {
40383
+ const heightValue = getUnit('height', config.height);
40384
+ styles.height = heightValue;
40385
+ listStyles.height = heightValue;
40386
+ listStyles.maxHeight = heightValue;
40387
+ }
40388
+ const controlClass = 'sortablelist-controls gn-control is-small has-arrange is-vertical is-center' + (config.buttonPosition === 'right' ? ' is-right' : '');
40389
+ const buttonDisabled = config.disabled || (config.data || []).length === 0;
40390
+ return (createElement$1("div", { id: this._uid, className: 'gn-sortablelist' + (config.disabled ? ' is-disabled' : ''), style: styles },
40391
+ createElement$1("div", { className: controlClass },
40392
+ createElement$1("button", { type: "button", className: "gn-button is-outline", "on-click": this._hidden.sort.bind(this, 'up-all'), disabled: buttonDisabled },
40393
+ createElement$1("span", { className: "gn-icon" },
40394
+ createElement$1("i", { className: "fa fa-light fa-angle-double-up" }))),
40395
+ createElement$1("button", { type: "button", className: "gn-button is-outline", "on-click": this._hidden.sort.bind(this, 'up'), disabled: buttonDisabled },
40396
+ createElement$1("span", { className: "gn-icon" },
40397
+ createElement$1("i", { className: "fa fa-light fa-angle-up" }))),
40398
+ createElement$1("button", { type: "button", className: "gn-button is-outline", "on-click": this._hidden.sort.bind(this, 'down'), disabled: buttonDisabled },
40399
+ createElement$1("span", { className: "gn-icon" },
40400
+ createElement$1("i", { className: "fa fa-light fa-angle-down" }))),
40401
+ createElement$1("button", { type: "button", className: "gn-button is-outline", "on-click": this._hidden.sort.bind(this, 'down-all'), disabled: buttonDisabled },
40402
+ createElement$1("span", { className: "gn-icon" },
40403
+ createElement$1("i", { className: "fa fa-light fa-angle-double-down" })))),
40404
+ createElement$1("div", { className: "gn-dropdown is-opened sortablelist-items" },
40405
+ createElement$1("div", { className: "dropdown-items", style: listStyles }, this._hidden.renderSub()))));
40406
+ }
40407
+ beforeMount() {
40408
+ this._hidden.validateData(this.$options.data);
40409
+ this._hidden.hydrateSelection(this.$options.data);
40410
+ }
40411
+ completed() {
40412
+ this._hidden.updateControls();
40413
+ }
40414
+ destroyed() {
40415
+ this._hidden.clearDragState();
40416
+ }
40417
+ }
40418
+
39759
40419
  class Splitter extends GNCoreInstance {
39760
40420
  constructor(name, selector, options = {}) {
39761
40421
  super(name, selector, options);
@@ -40403,6 +41063,7 @@ var gnUIComp = {
40403
41063
  picklist: Picklist,
40404
41064
  progressbar: Progressbar,
40405
41065
  selectbutton: SelectButton,
41066
+ sortablelist: SortableList,
40406
41067
  splitter: Splitter,
40407
41068
  switch: Switch,
40408
41069
  syntaxinput: SyntaxInput,