@ncds/ui-admin 1.8.3 → 1.8.5

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 (217) hide show
  1. package/dist/cjs/assets/scripts/featuredIcon.js +87 -0
  2. package/dist/cjs/assets/scripts/notification/FloatingNotification.js +178 -0
  3. package/dist/cjs/assets/scripts/notification/FullWidthNotification.js +133 -0
  4. package/dist/cjs/assets/scripts/notification/MessageNotification.js +159 -0
  5. package/dist/cjs/assets/scripts/notification/Notification.js +120 -0
  6. package/dist/cjs/assets/scripts/notification/const/classNames.js +50 -0
  7. package/dist/cjs/assets/scripts/notification/const/icons.js +31 -0
  8. package/dist/cjs/assets/scripts/notification/const/index.js +87 -0
  9. package/dist/cjs/assets/scripts/notification/const/sizes.js +46 -0
  10. package/dist/cjs/assets/scripts/notification/const/types.js +14 -0
  11. package/dist/cjs/assets/scripts/notification/index.js +116 -0
  12. package/dist/cjs/assets/scripts/notification/positionSync.js +180 -0
  13. package/dist/cjs/assets/scripts/notification/utils.js +122 -0
  14. package/dist/cjs/assets/scripts/shared/ButtonCloseX.js +45 -0
  15. package/dist/cjs/assets/scripts/utils/sanitize.js +39 -0
  16. package/dist/cjs/src/components/data-display/data-grid/DataGrid.js +5 -1
  17. package/dist/cjs/src/components/data-display/table/Table.js +118 -96
  18. package/dist/cjs/src/components/data-display/table/useTableScrollbars.js +187 -0
  19. package/dist/cjs/src/components/forms-and-input/combo-box/ComboBox.js +11 -10
  20. package/dist/cjs/src/components/forms-and-input/image-file-input/ImageFileInput.js +5 -2
  21. package/dist/cjs/src/components/forms-and-input/select-box/SelectBox.js +67 -29
  22. package/dist/cjs/src/components/index.js +33 -0
  23. package/dist/cjs/src/components/layout/block-container/BlockContainer.js +38 -0
  24. package/dist/cjs/src/components/layout/block-container/index.js +16 -0
  25. package/dist/cjs/src/components/layout/block-header/BlockHeader.js +107 -0
  26. package/dist/cjs/src/components/layout/block-header/SubTitle.js +56 -0
  27. package/dist/cjs/src/components/layout/block-header/index.js +27 -0
  28. package/dist/cjs/src/components/layout/page-title/PageTitle.js +95 -0
  29. package/dist/cjs/src/components/layout/page-title/index.js +16 -0
  30. package/dist/cjs/src/components/overlays/dropdown/Dropdown.js +47 -19
  31. package/dist/cjs/src/components/overlays/notification/CalloutNotification.js +25 -0
  32. package/dist/cjs/src/components/overlays/notification/FloatingNotification.js +86 -13
  33. package/dist/cjs/src/components/overlays/notification/Notification.js +7 -0
  34. package/dist/cjs/src/components/overlays/notification/host.js +12 -0
  35. package/dist/cjs/src/components/overlays/tooltip/Tooltip.js +57 -44
  36. package/dist/cjs/src/components/select-dropdown/SelectDropdown.js +2 -1
  37. package/dist/cjs/src/contexts/FloatingContext.js +11 -0
  38. package/dist/cjs/src/contexts/index.js +16 -0
  39. package/dist/cjs/src/hooks/index.js +11 -0
  40. package/dist/cjs/src/hooks/useFloatingPosition.js +78 -0
  41. package/dist/cjs/src/hooks/usePortalState.js +17 -0
  42. package/dist/cjs/src/utils/dropdown/maxSelection.js +35 -0
  43. package/dist/cjs/src/utils/dropdown/multiSelect.js +72 -15
  44. package/dist/esm/assets/scripts/featuredIcon.js +80 -0
  45. package/dist/esm/assets/scripts/notification/FloatingNotification.js +171 -0
  46. package/dist/esm/assets/scripts/notification/FullWidthNotification.js +126 -0
  47. package/dist/esm/assets/scripts/notification/MessageNotification.js +152 -0
  48. package/dist/esm/assets/scripts/notification/Notification.js +113 -0
  49. package/dist/esm/assets/scripts/notification/const/classNames.js +44 -0
  50. package/dist/esm/assets/scripts/notification/const/icons.js +25 -0
  51. package/dist/esm/assets/scripts/notification/const/index.js +4 -0
  52. package/dist/esm/assets/scripts/notification/const/sizes.js +40 -0
  53. package/dist/esm/assets/scripts/notification/const/types.js +8 -0
  54. package/dist/esm/assets/scripts/notification/index.js +10 -0
  55. package/dist/esm/assets/scripts/notification/positionSync.js +171 -0
  56. package/dist/esm/assets/scripts/notification/utils.js +109 -0
  57. package/dist/esm/assets/scripts/shared/ButtonCloseX.js +37 -0
  58. package/dist/esm/assets/scripts/utils/sanitize.js +31 -0
  59. package/dist/esm/src/components/data-display/data-grid/DataGrid.js +5 -1
  60. package/dist/esm/src/components/data-display/table/Table.js +118 -96
  61. package/dist/esm/src/components/data-display/table/useTableScrollbars.js +179 -0
  62. package/dist/esm/src/components/forms-and-input/combo-box/ComboBox.js +11 -10
  63. package/dist/esm/src/components/forms-and-input/image-file-input/ImageFileInput.js +5 -2
  64. package/dist/esm/src/components/forms-and-input/select-box/SelectBox.js +67 -29
  65. package/dist/esm/src/components/index.js +3 -0
  66. package/dist/esm/src/components/layout/block-container/BlockContainer.js +31 -0
  67. package/dist/esm/src/components/layout/block-container/index.js +1 -0
  68. package/dist/esm/src/components/layout/block-header/BlockHeader.js +100 -0
  69. package/dist/esm/src/components/layout/block-header/SubTitle.js +49 -0
  70. package/dist/esm/src/components/layout/block-header/index.js +2 -0
  71. package/dist/esm/src/components/layout/page-title/PageTitle.js +88 -0
  72. package/dist/esm/src/components/layout/page-title/index.js +1 -0
  73. package/dist/esm/src/components/overlays/dropdown/Dropdown.js +47 -19
  74. package/dist/esm/src/components/overlays/notification/CalloutNotification.js +19 -0
  75. package/dist/esm/src/components/overlays/notification/FloatingNotification.js +86 -14
  76. package/dist/esm/src/components/overlays/notification/Notification.js +7 -0
  77. package/dist/esm/src/components/overlays/notification/host.js +9 -0
  78. package/dist/esm/src/components/overlays/tooltip/Tooltip.js +58 -45
  79. package/dist/esm/src/components/select-dropdown/SelectDropdown.js +2 -1
  80. package/dist/esm/src/contexts/FloatingContext.js +4 -0
  81. package/dist/esm/src/contexts/index.js +1 -0
  82. package/dist/esm/src/hooks/index.js +1 -0
  83. package/dist/esm/src/hooks/useFloatingPosition.js +71 -0
  84. package/dist/esm/src/hooks/usePortalState.js +10 -0
  85. package/dist/esm/src/utils/dropdown/maxSelection.js +27 -0
  86. package/dist/esm/src/utils/dropdown/multiSelect.js +70 -14
  87. package/dist/temp/assets/scripts/featuredIcon.d.ts +22 -0
  88. package/dist/temp/assets/scripts/featuredIcon.js +79 -0
  89. package/dist/temp/assets/scripts/notification/FloatingNotification.d.ts +24 -0
  90. package/dist/temp/assets/scripts/notification/FloatingNotification.js +156 -0
  91. package/dist/temp/assets/scripts/notification/FullWidthNotification.d.ts +21 -0
  92. package/dist/temp/assets/scripts/notification/FullWidthNotification.js +111 -0
  93. package/dist/temp/assets/scripts/notification/MessageNotification.d.ts +22 -0
  94. package/dist/temp/assets/scripts/notification/MessageNotification.js +140 -0
  95. package/dist/temp/assets/scripts/notification/Notification.d.ts +22 -0
  96. package/dist/temp/assets/scripts/notification/Notification.js +112 -0
  97. package/dist/temp/assets/scripts/notification/const/classNames.d.ts +43 -0
  98. package/dist/temp/assets/scripts/notification/const/classNames.js +44 -0
  99. package/dist/temp/assets/scripts/notification/const/icons.d.ts +25 -0
  100. package/dist/temp/assets/scripts/notification/const/icons.js +25 -0
  101. package/dist/temp/assets/scripts/notification/const/index.d.ts +5 -0
  102. package/dist/temp/assets/scripts/notification/const/index.js +4 -0
  103. package/dist/temp/assets/scripts/notification/const/sizes.d.ts +32 -0
  104. package/dist/temp/assets/scripts/notification/const/sizes.js +40 -0
  105. package/dist/temp/assets/scripts/notification/const/types.d.ts +19 -0
  106. package/dist/temp/assets/scripts/notification/const/types.js +8 -0
  107. package/dist/temp/assets/scripts/notification/index.d.ts +8 -0
  108. package/dist/temp/assets/scripts/notification/index.js +10 -0
  109. package/dist/temp/assets/scripts/notification/positionSync.d.ts +50 -0
  110. package/dist/temp/assets/scripts/notification/positionSync.js +170 -0
  111. package/dist/temp/assets/scripts/notification/utils.d.ts +8 -0
  112. package/dist/temp/assets/scripts/notification/utils.js +115 -0
  113. package/dist/temp/assets/scripts/shared/ButtonCloseX.d.ts +5 -0
  114. package/dist/temp/assets/scripts/shared/ButtonCloseX.js +33 -0
  115. package/dist/temp/assets/scripts/utils/sanitize.d.ts +22 -0
  116. package/dist/temp/assets/scripts/utils/sanitize.js +31 -0
  117. package/dist/temp/src/components/data-display/data-grid/DataGrid.js +1 -1
  118. package/dist/temp/src/components/data-display/data-grid/DataGrid.types.d.ts +7 -0
  119. package/dist/temp/src/components/data-display/table/Table.d.ts +4 -1
  120. package/dist/temp/src/components/data-display/table/Table.js +53 -68
  121. package/dist/temp/src/components/data-display/table/types.d.ts +18 -0
  122. package/dist/temp/src/components/data-display/table/useTableScrollbars.d.ts +25 -0
  123. package/dist/temp/src/components/data-display/table/useTableScrollbars.js +136 -0
  124. package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.d.ts +8 -0
  125. package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.js +7 -11
  126. package/dist/temp/src/components/forms-and-input/image-file-input/ImageFileInput.js +1 -1
  127. package/dist/temp/src/components/forms-and-input/select-box/SelectBox.d.ts +13 -0
  128. package/dist/temp/src/components/forms-and-input/select-box/SelectBox.js +30 -3
  129. package/dist/temp/src/components/index.d.ts +3 -0
  130. package/dist/temp/src/components/index.js +3 -0
  131. package/dist/temp/src/components/layout/block-container/BlockContainer.d.ts +19 -0
  132. package/dist/temp/src/components/layout/block-container/BlockContainer.js +11 -0
  133. package/dist/temp/src/components/layout/block-container/index.d.ts +1 -0
  134. package/dist/temp/src/components/layout/block-container/index.js +1 -0
  135. package/dist/temp/src/components/layout/block-header/BlockHeader.d.ts +23 -0
  136. package/dist/temp/src/components/layout/block-header/BlockHeader.js +21 -0
  137. package/dist/temp/src/components/layout/block-header/SubTitle.d.ts +19 -0
  138. package/dist/temp/src/components/layout/block-header/SubTitle.js +8 -0
  139. package/dist/temp/src/components/layout/block-header/index.d.ts +2 -0
  140. package/dist/temp/src/components/layout/block-header/index.js +2 -0
  141. package/dist/temp/src/components/layout/page-title/PageTitle.d.ts +22 -0
  142. package/dist/temp/src/components/layout/page-title/PageTitle.js +19 -0
  143. package/dist/temp/src/components/layout/page-title/index.d.ts +1 -0
  144. package/dist/temp/src/components/layout/page-title/index.js +1 -0
  145. package/dist/temp/src/components/overlays/dropdown/Dropdown.d.ts +5 -0
  146. package/dist/temp/src/components/overlays/dropdown/Dropdown.js +35 -11
  147. package/dist/temp/src/components/overlays/notification/CalloutNotification.d.ts +9 -0
  148. package/dist/temp/src/components/overlays/notification/CalloutNotification.js +6 -0
  149. package/dist/temp/src/components/overlays/notification/FloatingNotification.d.ts +15 -0
  150. package/dist/temp/src/components/overlays/notification/FloatingNotification.js +81 -13
  151. package/dist/temp/src/components/overlays/notification/Notification.d.ts +18 -3
  152. package/dist/temp/src/components/overlays/notification/Notification.js +4 -0
  153. package/dist/temp/src/components/overlays/notification/host.d.ts +9 -0
  154. package/dist/temp/src/components/overlays/notification/host.js +9 -0
  155. package/dist/temp/src/components/overlays/tooltip/Tooltip.d.ts +5 -1
  156. package/dist/temp/src/components/overlays/tooltip/Tooltip.js +25 -22
  157. package/dist/temp/src/components/select-dropdown/SelectDropdown.d.ts +6 -0
  158. package/dist/temp/src/components/select-dropdown/SelectDropdown.js +2 -2
  159. package/dist/temp/src/contexts/FloatingContext.d.ts +6 -0
  160. package/dist/temp/src/contexts/FloatingContext.js +4 -0
  161. package/dist/temp/src/contexts/index.d.ts +1 -0
  162. package/dist/temp/src/contexts/index.js +1 -0
  163. package/dist/temp/src/hooks/index.d.ts +1 -0
  164. package/dist/temp/src/hooks/index.js +1 -0
  165. package/dist/temp/src/hooks/useFloatingPosition.d.ts +19 -0
  166. package/dist/temp/src/hooks/useFloatingPosition.js +55 -0
  167. package/dist/temp/src/hooks/usePortalState.d.ts +6 -0
  168. package/dist/temp/src/hooks/usePortalState.js +7 -0
  169. package/dist/temp/src/utils/dropdown/maxSelection.d.ts +24 -0
  170. package/dist/temp/src/utils/dropdown/maxSelection.js +28 -0
  171. package/dist/temp/src/utils/dropdown/multiSelect.d.ts +42 -2
  172. package/dist/temp/src/utils/dropdown/multiSelect.js +66 -13
  173. package/dist/types/assets/scripts/featuredIcon.d.ts +22 -0
  174. package/dist/types/assets/scripts/notification/FloatingNotification.d.ts +24 -0
  175. package/dist/types/assets/scripts/notification/FullWidthNotification.d.ts +21 -0
  176. package/dist/types/assets/scripts/notification/MessageNotification.d.ts +22 -0
  177. package/dist/types/assets/scripts/notification/Notification.d.ts +22 -0
  178. package/dist/types/assets/scripts/notification/const/classNames.d.ts +43 -0
  179. package/dist/types/assets/scripts/notification/const/icons.d.ts +25 -0
  180. package/dist/types/assets/scripts/notification/const/index.d.ts +5 -0
  181. package/dist/types/assets/scripts/notification/const/sizes.d.ts +32 -0
  182. package/dist/types/assets/scripts/notification/const/types.d.ts +19 -0
  183. package/dist/types/assets/scripts/notification/index.d.ts +8 -0
  184. package/dist/types/assets/scripts/notification/positionSync.d.ts +50 -0
  185. package/dist/types/assets/scripts/notification/utils.d.ts +8 -0
  186. package/dist/types/assets/scripts/shared/ButtonCloseX.d.ts +5 -0
  187. package/dist/types/assets/scripts/utils/sanitize.d.ts +22 -0
  188. package/dist/types/src/components/data-display/data-grid/DataGrid.types.d.ts +7 -0
  189. package/dist/types/src/components/data-display/table/Table.d.ts +4 -1
  190. package/dist/types/src/components/data-display/table/types.d.ts +18 -0
  191. package/dist/types/src/components/data-display/table/useTableScrollbars.d.ts +25 -0
  192. package/dist/types/src/components/forms-and-input/combo-box/ComboBox.d.ts +8 -0
  193. package/dist/types/src/components/forms-and-input/select-box/SelectBox.d.ts +13 -0
  194. package/dist/types/src/components/index.d.ts +3 -0
  195. package/dist/types/src/components/layout/block-container/BlockContainer.d.ts +19 -0
  196. package/dist/types/src/components/layout/block-container/index.d.ts +1 -0
  197. package/dist/types/src/components/layout/block-header/BlockHeader.d.ts +23 -0
  198. package/dist/types/src/components/layout/block-header/SubTitle.d.ts +19 -0
  199. package/dist/types/src/components/layout/block-header/index.d.ts +2 -0
  200. package/dist/types/src/components/layout/page-title/PageTitle.d.ts +22 -0
  201. package/dist/types/src/components/layout/page-title/index.d.ts +1 -0
  202. package/dist/types/src/components/overlays/dropdown/Dropdown.d.ts +5 -0
  203. package/dist/types/src/components/overlays/notification/CalloutNotification.d.ts +9 -0
  204. package/dist/types/src/components/overlays/notification/FloatingNotification.d.ts +15 -0
  205. package/dist/types/src/components/overlays/notification/Notification.d.ts +18 -3
  206. package/dist/types/src/components/overlays/notification/host.d.ts +9 -0
  207. package/dist/types/src/components/overlays/tooltip/Tooltip.d.ts +5 -1
  208. package/dist/types/src/components/select-dropdown/SelectDropdown.d.ts +6 -0
  209. package/dist/types/src/contexts/FloatingContext.d.ts +6 -0
  210. package/dist/types/src/contexts/index.d.ts +1 -0
  211. package/dist/types/src/hooks/index.d.ts +1 -0
  212. package/dist/types/src/hooks/useFloatingPosition.d.ts +19 -0
  213. package/dist/types/src/hooks/usePortalState.d.ts +6 -0
  214. package/dist/types/src/utils/dropdown/maxSelection.d.ts +24 -0
  215. package/dist/types/src/utils/dropdown/multiSelect.d.ts +42 -2
  216. package/dist/ui-admin/assets/styles/style.css +596 -64
  217. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.usePortalState = void 0;
7
+ var _FloatingContext = require("../contexts/FloatingContext");
8
+ const usePortalState = usePortalProp => {
9
+ const floatingContext = (0, _FloatingContext.useFloatingContext)();
10
+ const shouldPortal = usePortalProp ?? floatingContext?.preferPortal ?? false;
11
+ const portalContainer = floatingContext?.portalContainer ?? (typeof document !== 'undefined' ? document.body : null);
12
+ return {
13
+ shouldPortal,
14
+ portalContainer
15
+ };
16
+ };
17
+ exports.usePortalState = usePortalState;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isMaxSelectionReached = exports.isMaxSelectionLimitActive = void 0;
7
+ /**
8
+ * SelectBox / ComboBox 공통 — 최대 선택 개수 제한 활성 여부 판정.
9
+ *
10
+ * React 훅(`useMultiSelect`)과 vanilla 드롭다운(`DropdownModel`)가 동일 로직을 공유하므로
11
+ * 두 곳에 정의가 갈라지지 않도록 단일 source-of-truth로 둔다.
12
+ *
13
+ * **계약**
14
+ * - `0` 이상의 정수 → 활성 (단, `0`은 모든 새 선택을 차단하는 의미)
15
+ * - 음수 / 비정수 / `undefined` / `null` → 비활성 (제한 없는 것과 동일)
16
+ *
17
+ * `0`을 활성으로 보는 이유: 호출자가 `maxSelection={0}`을 명시적으로 전달했을 때
18
+ * "선택 자체를 막겠다"는 의도로 해석하기 위함. 음수는 일반적으로 "값 없음"의 sentinel이므로 비활성.
19
+ */
20
+ const isMaxSelectionLimitActive = maxSelection => typeof maxSelection === 'number' && Number.isInteger(maxSelection) && maxSelection >= 0;
21
+ /**
22
+ * 현재 선택 길이가 최대치에 도달했는지 단일 함수로 판정.
23
+ *
24
+ * 호출 측에서 `>=`을 직접 작성하지 않도록 의미를 노출하고, type predicate가 내부에서 적용되어
25
+ * `maxSelection`의 narrowing이 보장된다 (외부 predicate 호출로 인한 narrowing 누락 방지).
26
+ *
27
+ * - 제한 비활성(음수 / null / undefined / 비정수): 항상 `false`.
28
+ * - 활성: `currentLength >= maxSelection`.
29
+ */
30
+ exports.isMaxSelectionLimitActive = isMaxSelectionLimitActive;
31
+ const isMaxSelectionReached = (currentLength, maxSelection) => {
32
+ if (!isMaxSelectionLimitActive(maxSelection)) return false;
33
+ return currentLength >= maxSelection;
34
+ };
35
+ exports.isMaxSelectionReached = isMaxSelectionReached;
@@ -3,7 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.useMultiSelect = exports.removeTagFromSelected = exports.isAllItemsSelected = exports.handleSelectAllItems = exports.getSelectedTags = exports.getSelectAllButtonText = void 0;
6
+ exports.useMultiSelect = exports.useMaxSelection = exports.removeTagFromSelected = exports.isAllItemsSelected = exports.handleSelectAllItems = exports.getSelectedTags = exports.getSelectAllButtonText = void 0;
7
+ var _react = require("react");
8
+ var _maxSelection = require("./maxSelection");
7
9
  /**
8
10
  * 전체 선택 상태를 확인하는 유틸 함수
9
11
  */
@@ -54,27 +56,82 @@ const removeTagFromSelected = (selectedValues, tagIdToRemove) => {
54
56
  return selectedValues.filter(id => id !== tagIdToRemove);
55
57
  };
56
58
  /**
57
- * 전체 선택 관련 로직을 번에 처리하는 커스텀 훅
59
+ * 최대 선택 개수 관련 응집을 별도 훅으로 분리.
60
+ *
61
+ * - `isMaxSelectionActive`: 제한 활성 여부.
62
+ * - `isMaxReached`: 현재 선택 길이가 최대치에 도달했는지.
63
+ * - `canAdd(currentLength)`: 새 항목 추가가 허용되는지 (도달 시 `false`).
64
+ *
65
+ * 도메인 규칙(`>=` 비교 등)을 외부로 노출하지 않고 헬퍼를 통해 의미만 반환한다.
66
+ * `useMultiSelect`가 내부에서 사용하지만, 단독으로도 필요한 호출자가 쓸 수 있게 export한다.
58
67
  */
59
68
  exports.removeTagFromSelected = removeTagFromSelected;
60
- const useMultiSelect = (selectedValues, options, selectText, deselectText) => {
69
+ const useMaxSelection = (selectedValues, maxSelection) => {
70
+ const isMaxSelectionActive = (0, _maxSelection.isMaxSelectionLimitActive)(maxSelection);
71
+ const isMaxReached = (0, _maxSelection.isMaxSelectionReached)(selectedValues.length, maxSelection);
72
+ const canAdd = (0, _react.useCallback)(currentLength => !(0, _maxSelection.isMaxSelectionReached)(currentLength, maxSelection), [maxSelection]);
73
+ return {
74
+ isMaxSelectionActive,
75
+ isMaxReached,
76
+ canAdd
77
+ };
78
+ };
79
+ /**
80
+ * 전체 선택 관련 로직을 한 번에 처리하는 커스텀 훅.
81
+ *
82
+ * 시그니처는 호출자가 maxSelection만 지정하더라도 텍스트 라벨을 건너뛰지 않도록 옵션 객체로 받는다.
83
+ *
84
+ * **반환의 `tryToggle`**: multiple 토글 결과를 직접 돌려준다.
85
+ * - 새 배열 반환: 정상 토글 (추가 또는 해제).
86
+ * - `null` 반환: 최대 개수 도달로 추가가 차단됨 (호출자는 변경 없이 종료해야 함).
87
+ *
88
+ * 호출자가 `>= maxSelection` 같은 도메인 규칙을 직접 검사하지 않도록 결과만 노출한다.
89
+ */
90
+ exports.useMaxSelection = useMaxSelection;
91
+ const useMultiSelect = function (selectedValues, options) {
92
+ let config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
93
+ const {
94
+ selectText,
95
+ deselectText,
96
+ maxSelection
97
+ } = config;
61
98
  const isAllSelected = isAllItemsSelected(selectedValues, options);
62
99
  const buttonText = getSelectAllButtonText(selectedValues, options, selectText, deselectText);
63
- const toggleSelectAll = () => {
64
- return handleSelectAllItems(selectedValues, options);
65
- };
66
- const getSelectedTagsData = () => {
67
- return getSelectedTags(selectedValues, options);
68
- };
69
- const removeTag = tagId => {
70
- return removeTagFromSelected(selectedValues, tagId);
71
- };
72
- return {
100
+ const {
101
+ isMaxSelectionActive,
102
+ isMaxReached,
103
+ canAdd
104
+ } = useMaxSelection(selectedValues, maxSelection);
105
+ const toggleSelectAll = (0, _react.useCallback)(() => handleSelectAllItems(selectedValues, options), [selectedValues, options]);
106
+ const getSelectedTagsData = (0, _react.useCallback)(() => getSelectedTags(selectedValues, options), [selectedValues, options]);
107
+ const removeTag = (0, _react.useCallback)(tagId => removeTagFromSelected(selectedValues, tagId), [selectedValues]);
108
+ /**
109
+ * multiple 선택 토글을 한 번에 처리한다.
110
+ * - 이미 포함된 항목: 해제 (배열에서 제거하여 반환).
111
+ * - 미포함 항목: 추가 시도. 최대치 도달이면 `null` 반환(차단).
112
+ *
113
+ * 호출 측은 `null` 여부만 확인하면 되며 maxSelection 도메인 규칙을 알 필요가 없다.
114
+ */
115
+ const tryToggle = (0, _react.useCallback)((optionId, currentArray) => {
116
+ const base = Array.isArray(currentArray) ? currentArray : [];
117
+ const idx = base.indexOf(optionId);
118
+ if (idx > -1) {
119
+ const next = [...base];
120
+ next.splice(idx, 1);
121
+ return next;
122
+ }
123
+ if (!canAdd(base.length)) return null;
124
+ return [...base, optionId];
125
+ }, [canAdd]);
126
+ return (0, _react.useMemo)(() => ({
73
127
  isAllSelected,
74
128
  buttonText,
75
129
  toggleSelectAll,
76
130
  getSelectedTagsData,
77
- removeTag
78
- };
131
+ removeTag,
132
+ isMaxSelectionActive,
133
+ isMaxReached,
134
+ tryToggle
135
+ }), [isAllSelected, buttonText, toggleSelectAll, getSelectedTagsData, removeTag, isMaxSelectionActive, isMaxReached, tryToggle]);
79
136
  };
80
137
  exports.useMultiSelect = useMultiSelect;
@@ -0,0 +1,80 @@
1
+ // React 컴포넌트와 동일하게 구성
2
+ const iconSizeMap = {
3
+ sm: 16,
4
+ md: 20,
5
+ lg: 24,
6
+ xl: 28
7
+ };
8
+ export class FeaturedIcon {
9
+ constructor(options) {
10
+ this.options = {
11
+ theme: 'light-circle',
12
+ color: 'neutral',
13
+ size: 'sm',
14
+ className: '',
15
+ ...options
16
+ };
17
+ this.element = this.createElement();
18
+ }
19
+ // svgString ( ICON SVG ) 을 받아서 요소를 생성
20
+ createElement() {
21
+ const {
22
+ theme,
23
+ color,
24
+ size,
25
+ className,
26
+ svgString
27
+ } = this.options;
28
+ // React FeaturedIcon과 동일한 클래스 구조
29
+ const classes = ['ncua-featured-icon', `ncua-featured-icon--${theme}`, `ncua-featured-icon--${color}`, `ncua-featured-icon--${size}`];
30
+ if (className) {
31
+ classes.push(className);
32
+ }
33
+ const div = document.createElement('div');
34
+ div.className = classes.join(' ');
35
+ // outline-circle 테마일 때 추가 요소들 (React 컴포넌트와 동일)
36
+ if (theme === 'outline-circle') {
37
+ const innerOutline = document.createElement('div');
38
+ innerOutline.className = 'ncua-featured-icon__outline ncua-featured-icon__outline--inner';
39
+ div.appendChild(innerOutline);
40
+ const outerOutline = document.createElement('div');
41
+ outerOutline.className = 'ncua-featured-icon__outline ncua-featured-icon__outline--outer';
42
+ div.appendChild(outerOutline);
43
+ }
44
+ // SVG 아이콘 추가
45
+ const iconSize = iconSizeMap[size];
46
+ const svgWithSize = this.addSizeToSvg(svgString, iconSize);
47
+ div.innerHTML += svgWithSize;
48
+ return div;
49
+ }
50
+ addSizeToSvg(svgString, size) {
51
+ return svgString.replace(/<svg([^>]*)>/, `<svg$1 width="${size}" height="${size}">`);
52
+ }
53
+ // Public methods
54
+ getElement() {
55
+ return this.element;
56
+ }
57
+ updateColor(color) {
58
+ this.element.className = this.element.className.replace(/ncua-featured-icon--(neutral|error|warning|success)/, `ncua-featured-icon--${color}`);
59
+ this.options.color = color;
60
+ }
61
+ updateSize(size) {
62
+ const iconSize = iconSizeMap[size];
63
+ this.element.className = this.element.className.replace(/ncua-featured-icon--(sm|md|lg|xl)/, `ncua-featured-icon--${size}`);
64
+ const svgIcon = this.element.querySelector('svg');
65
+ if (svgIcon) {
66
+ svgIcon.setAttribute('width', String(iconSize));
67
+ svgIcon.setAttribute('height', String(iconSize));
68
+ }
69
+ this.options.size = size;
70
+ }
71
+ destroy() {
72
+ if (this.element.parentNode) {
73
+ this.element.parentNode.removeChild(this.element);
74
+ }
75
+ }
76
+ // Static factory method
77
+ static create(options) {
78
+ return new FeaturedIcon(options);
79
+ }
80
+ }
@@ -0,0 +1,171 @@
1
+ import { MEDIA_QUERY } from '../../../src/constant/breakpoint';
2
+ import { FeaturedIcon } from '../featuredIcon';
3
+ import { ButtonCloseX } from '../shared/ButtonCloseX';
4
+ import { sanitizeHtml } from '../utils/sanitize';
5
+ import { CLASS_NAMES, FLOATING_ICON_MAP, getSizes } from './const';
6
+ import { bindNotificationEvents, createWrapperElement, isMobile, renderActions, renderSupportingText, setupAutoClose } from './utils';
7
+ export class FloatingNotification {
8
+ constructor(options) {
9
+ this.options = {
10
+ color: 'neutral',
11
+ className: '',
12
+ actions: [],
13
+ autoClose: 0,
14
+ supportingText: undefined,
15
+ ...options
16
+ };
17
+ this.element = this.createElement();
18
+ this.bindEvents();
19
+ this.setupAutoClose();
20
+ this.setupMobileListener();
21
+ }
22
+ createElement() {
23
+ const {
24
+ title,
25
+ supportingText,
26
+ color,
27
+ className,
28
+ actions,
29
+ onClose
30
+ } = this.options;
31
+ // 플로팅 알림에서 info 색상 사용 시 neutral로 대체
32
+ let actualColor = color;
33
+ if (color === 'info') {
34
+ actualColor = 'neutral';
35
+ }
36
+ const wrapper = createWrapperElement(CLASS_NAMES.FLOATING.BASE, actualColor, className);
37
+ const iconFunction = FLOATING_ICON_MAP[actualColor];
38
+ const mobile = isMobile();
39
+ // FeaturedIcon 생성
40
+ let featuredIconElement = null;
41
+ if (iconFunction) {
42
+ const size = getSizes.featuredIcon(mobile);
43
+ const iconSize = getSizes.iconPixel(mobile);
44
+ const iconSvg = iconFunction(iconSize);
45
+ this.featuredIcon = FeaturedIcon.create({
46
+ svgString: iconSvg,
47
+ theme: 'dark-circle',
48
+ color: actualColor,
49
+ size: size
50
+ });
51
+ featuredIconElement = this.featuredIcon.getElement();
52
+ }
53
+ wrapper.innerHTML = sanitizeHtml(this.buildTemplate({
54
+ title,
55
+ supportingText,
56
+ actions,
57
+ onClose,
58
+ isMobile: mobile
59
+ }));
60
+ // FeaturedIcon을 container에 추가
61
+ if (featuredIconElement) {
62
+ const container = wrapper.querySelector(`.${CLASS_NAMES.FLOATING.CONTAINER}`);
63
+ if (container) {
64
+ container.insertBefore(featuredIconElement, container.firstChild);
65
+ }
66
+ }
67
+ return wrapper;
68
+ }
69
+ buildTemplate(params) {
70
+ const {
71
+ title,
72
+ supportingText,
73
+ actions,
74
+ onClose,
75
+ isMobile
76
+ } = params;
77
+ return `
78
+ <div class="${CLASS_NAMES.FLOATING.CONTENT}">
79
+ <div class="${CLASS_NAMES.FLOATING.CONTAINER}">
80
+ <div class="${CLASS_NAMES.FLOATING.TEXT_CONTAINER}">
81
+ <div class="${CLASS_NAMES.FLOATING.TITLE_WRAPPER}">
82
+ <span class="${CLASS_NAMES.FLOATING.TITLE}">${title}</span>
83
+ </div>
84
+ ${renderSupportingText(supportingText, CLASS_NAMES.FLOATING.SUPPORTING_TEXT)}
85
+ ${actions && actions.length > 0 ? renderActions(actions, CLASS_NAMES.FLOATING.ACTIONS) : ''}
86
+ </div>
87
+ </div>
88
+ </div>
89
+ ${this.renderCloseButton(onClose, isMobile)}
90
+ `;
91
+ }
92
+ renderCloseButton(onClose) {
93
+ let isMobile = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
94
+ if (!onClose) return '';
95
+ const size = getSizes.closeButton(isMobile);
96
+ return ButtonCloseX(size, 'light', CLASS_NAMES.FLOATING.CLOSE_BUTTON, '알림 닫기', onClose);
97
+ }
98
+ setupMobileListener() {
99
+ const mediaQuery = window.matchMedia(MEDIA_QUERY.mobile);
100
+ const handleChange = e => {
101
+ this.updateMobileStyles(e.matches);
102
+ if (e.matches && this.options.onClose) {
103
+ this.options.onClose();
104
+ this.remove();
105
+ }
106
+ };
107
+ handleChange({
108
+ matches: mediaQuery.matches
109
+ });
110
+ mediaQuery.addEventListener('change', handleChange);
111
+ this.mobileCleanup = () => {
112
+ mediaQuery.removeEventListener('change', handleChange);
113
+ };
114
+ }
115
+ updateMobileStyles(mobile) {
116
+ if (this.featuredIcon) {
117
+ const newSize = getSizes.featuredIcon(mobile);
118
+ this.featuredIcon.updateSize(newSize);
119
+ }
120
+ const closeButton = this.element.querySelector('.ncua-button-close-x');
121
+ if (closeButton) {
122
+ const newSize = getSizes.closeButton(mobile);
123
+ const newSvgSize = getSizes.closeButtonSvg(newSize);
124
+ closeButton.className = closeButton.className.replace(/ncua-button-close-x--(xs|sm)/, `ncua-button-close-x--${newSize}`);
125
+ const svg = closeButton.querySelector('svg');
126
+ if (svg) {
127
+ svg.setAttribute('width', newSvgSize.toString());
128
+ svg.setAttribute('height', newSvgSize.toString());
129
+ }
130
+ }
131
+ }
132
+ bindEvents() {
133
+ bindNotificationEvents(this.element, this.options.actions, this.options.onClose, () => this.remove());
134
+ }
135
+ setupAutoClose() {
136
+ this.autoCloseTimer = setupAutoClose(this.options.autoClose, this.options.onClose, () => this.remove());
137
+ }
138
+ // Public methods
139
+ getElement() {
140
+ return this.element;
141
+ }
142
+ appendTo(parent) {
143
+ parent.appendChild(this.element);
144
+ }
145
+ remove() {
146
+ if (this.autoCloseTimer) {
147
+ clearTimeout(this.autoCloseTimer);
148
+ this.autoCloseTimer = undefined;
149
+ }
150
+ if (this.element?.parentNode) {
151
+ this.element.parentNode.removeChild(this.element);
152
+ }
153
+ }
154
+ destroy() {
155
+ // FeaturedIcon 정리
156
+ if (this.featuredIcon) {
157
+ this.featuredIcon.destroy();
158
+ this.featuredIcon = undefined;
159
+ }
160
+ // 모바일 리스너 정리
161
+ if (this.mobileCleanup) {
162
+ this.mobileCleanup();
163
+ this.mobileCleanup = undefined;
164
+ }
165
+ this.remove();
166
+ }
167
+ // Static factory methods
168
+ static create(options) {
169
+ return new FloatingNotification(options);
170
+ }
171
+ }
@@ -0,0 +1,126 @@
1
+ import { sanitizeHtml } from '../utils/sanitize';
2
+ import { CLASS_NAMES, FULL_WIDTH_ICON_MAP, FULL_WIDTH_SIZES, SVG_ICONS } from './const';
3
+ import { bindNotificationEvents, createWrapperElement, renderActions, renderSupportingText, setupAutoClose } from './utils';
4
+ export class FullWidthNotification {
5
+ constructor(options) {
6
+ this.options = {
7
+ color: 'neutral',
8
+ className: '',
9
+ actions: [],
10
+ autoClose: 0,
11
+ supportingText: undefined,
12
+ ...options
13
+ };
14
+ this.element = this.createElement();
15
+ this.bindEvents();
16
+ this.setupAutoClose();
17
+ }
18
+ createElement() {
19
+ const {
20
+ title,
21
+ supportingText,
22
+ color,
23
+ className,
24
+ actions,
25
+ onClose,
26
+ supportTextLink,
27
+ onHidePermanently
28
+ } = this.options;
29
+ const wrapper = createWrapperElement(CLASS_NAMES.FULL_WIDTH.BASE, color, className);
30
+ const iconHtml = FULL_WIDTH_ICON_MAP[color](FULL_WIDTH_SIZES.ICON);
31
+ wrapper.innerHTML = sanitizeHtml(this.buildTemplate({
32
+ iconHtml,
33
+ title,
34
+ supportingText,
35
+ actions,
36
+ onClose,
37
+ supportTextLink,
38
+ onHidePermanently
39
+ }));
40
+ return wrapper;
41
+ }
42
+ buildTemplate(params) {
43
+ const {
44
+ iconHtml,
45
+ title,
46
+ supportingText,
47
+ actions,
48
+ onClose,
49
+ supportTextLink,
50
+ onHidePermanently
51
+ } = params;
52
+ return `
53
+ <div class="${CLASS_NAMES.FULL_WIDTH.CONTAINER}">
54
+ <div class="${CLASS_NAMES.FULL_WIDTH.CONTENT}">
55
+ <div class="${CLASS_NAMES.FULL_WIDTH.CONTENT_WRAPPER}">
56
+ <div class="${CLASS_NAMES.FULL_WIDTH.ICON}">${iconHtml}</div>
57
+ <div class="${CLASS_NAMES.FULL_WIDTH.TEXT_CONTAINER}">
58
+ <span class="${CLASS_NAMES.FULL_WIDTH.TITLE}">${title}</span>
59
+ ${renderSupportingText(supportingText, CLASS_NAMES.FULL_WIDTH.SUPPORTING_TEXT, supportTextLink)}
60
+ </div>
61
+ </div>
62
+ <div class="${CLASS_NAMES.FULL_WIDTH.ACTIONS_CONTAINER}">
63
+ ${actions && actions.length > 0 ? renderActions(actions, CLASS_NAMES.FULL_WIDTH.ACTIONS) : ''}
64
+ ${this.renderHidePermanentlyButton(onHidePermanently)}
65
+ ${this.renderCloseButton(onClose)}
66
+ </div>
67
+ </div>
68
+ </div>
69
+ `;
70
+ }
71
+ renderHidePermanentlyButton(onHidePermanently) {
72
+ if (!onHidePermanently) return '';
73
+ return `
74
+ <button type="button" class="ncua-notification__action-button ncua-notification__action-button--text ncua-full-width-notification__link" data-hide-permanently="true">
75
+ 다시보지 않기
76
+ </button>
77
+ `;
78
+ }
79
+ renderCloseButton(onClose) {
80
+ if (!onClose) return '';
81
+ return `
82
+ <button type="button" class="${CLASS_NAMES.FULL_WIDTH.CLOSE_BUTTON}" aria-label="알림 닫기">
83
+ ${SVG_ICONS['x-close'](FULL_WIDTH_SIZES.CLOSE_BUTTON)}
84
+ </button>
85
+ `;
86
+ }
87
+ bindEvents() {
88
+ bindNotificationEvents(this.element, this.options.actions, this.options.onClose, () => this.remove());
89
+ // 다시보지 않기 버튼 이벤트 바인딩
90
+ if (this.options.onHidePermanently) {
91
+ const hidePermanentlyButton = this.element.querySelector('[data-hide-permanently="true"]');
92
+ if (hidePermanentlyButton) {
93
+ hidePermanentlyButton.addEventListener('click', () => {
94
+ this.options.onHidePermanently?.();
95
+ this.remove();
96
+ });
97
+ }
98
+ }
99
+ }
100
+ setupAutoClose() {
101
+ this.autoCloseTimer = setupAutoClose(this.options.autoClose, this.options.onClose, () => this.remove());
102
+ }
103
+ // Public methods
104
+ getElement() {
105
+ return this.element;
106
+ }
107
+ appendTo(parent) {
108
+ parent.appendChild(this.element);
109
+ }
110
+ remove() {
111
+ if (this.autoCloseTimer) {
112
+ clearTimeout(this.autoCloseTimer);
113
+ this.autoCloseTimer = undefined;
114
+ }
115
+ if (this.element?.parentNode) {
116
+ this.element.parentNode.removeChild(this.element);
117
+ }
118
+ }
119
+ destroy() {
120
+ this.remove();
121
+ }
122
+ // Static factory methods
123
+ static create(options) {
124
+ return new FullWidthNotification(options);
125
+ }
126
+ }
@@ -0,0 +1,152 @@
1
+ import { FeaturedIcon } from '../featuredIcon';
2
+ import { sanitizeHtml } from '../utils/sanitize';
3
+ import { CLASS_NAMES, FLOATING_ICON_MAP, MESSAGE_SIZES, SVG_ICONS } from './const';
4
+ import { bindNotificationEvents, createWrapperElement, renderActions, renderSupportingText, setupAutoClose } from './utils';
5
+ export class MessageNotification {
6
+ constructor(options) {
7
+ this.options = {
8
+ color: 'neutral',
9
+ className: '',
10
+ actions: [],
11
+ autoClose: 0,
12
+ supportingText: undefined,
13
+ ...options
14
+ };
15
+ this.element = this.createElement();
16
+ this.bindEvents();
17
+ this.setupAutoClose();
18
+ }
19
+ createElement() {
20
+ const {
21
+ title,
22
+ supportingText,
23
+ color,
24
+ className,
25
+ actions,
26
+ onClose,
27
+ onHidePermanently
28
+ } = this.options;
29
+ // message 타입은 neutral, error, warning, success 4가지 색상만 지원
30
+ let actualColor = color;
31
+ if (color === 'info') {
32
+ actualColor = 'neutral';
33
+ }
34
+ const wrapper = createWrapperElement(CLASS_NAMES.MESSAGE.BASE, actualColor, className);
35
+ const iconFunction = FLOATING_ICON_MAP[actualColor];
36
+ // FeaturedIcon 생성
37
+ let featuredIconElement = null;
38
+ if (iconFunction) {
39
+ const iconSvg = iconFunction(MESSAGE_SIZES.ICON_PIXEL);
40
+ this.featuredIcon = FeaturedIcon.create({
41
+ svgString: iconSvg,
42
+ theme: 'light-circle',
43
+ color: actualColor,
44
+ size: MESSAGE_SIZES.FEATURED_ICON
45
+ });
46
+ featuredIconElement = this.featuredIcon.getElement();
47
+ }
48
+ wrapper.innerHTML = sanitizeHtml(this.buildTemplate({
49
+ title,
50
+ supportingText,
51
+ actions,
52
+ onClose,
53
+ onHidePermanently
54
+ }));
55
+ // FeaturedIcon을 content-wrapper에 추가
56
+ if (featuredIconElement) {
57
+ const contentWrapper = wrapper.querySelector(`.${CLASS_NAMES.MESSAGE.CONTENT_WRAPPER}`);
58
+ if (contentWrapper) {
59
+ contentWrapper.insertBefore(featuredIconElement, contentWrapper.firstChild);
60
+ }
61
+ }
62
+ return wrapper;
63
+ }
64
+ buildTemplate(params) {
65
+ const {
66
+ title,
67
+ supportingText,
68
+ actions,
69
+ onClose,
70
+ onHidePermanently
71
+ } = params;
72
+ return `
73
+ <div class="${CLASS_NAMES.MESSAGE.CONTAINER}">
74
+ <div class="${CLASS_NAMES.MESSAGE.CONTENT}">
75
+ <div class="${CLASS_NAMES.MESSAGE.CONTENT_WRAPPER}">
76
+ <div class="${CLASS_NAMES.MESSAGE.TEXT_CONTAINER}">
77
+ <span class="${CLASS_NAMES.MESSAGE.TITLE}">${title}</span>
78
+ ${renderSupportingText(supportingText, CLASS_NAMES.MESSAGE.SUPPORTING_TEXT)}
79
+ </div>
80
+ </div>
81
+ <div class="${CLASS_NAMES.MESSAGE.ACTIONS_CONTAINER}">
82
+ ${actions && actions.length > 0 ? renderActions(actions, CLASS_NAMES.MESSAGE.ACTIONS) : ''}
83
+ </div>
84
+ <div class="${CLASS_NAMES.MESSAGE.ACTIONS_CONTAINER}">
85
+ ${this.renderHidePermanentlyButton(onHidePermanently)}
86
+ ${this.renderCloseButton(onClose)}
87
+ </div>
88
+ </div>
89
+ </div>
90
+ `;
91
+ }
92
+ renderHidePermanentlyButton(onHidePermanently) {
93
+ if (!onHidePermanently) return '';
94
+ return `
95
+ <button type="button" class="${CLASS_NAMES.COMMON.ACTION_BUTTON} ${CLASS_NAMES.COMMON.ACTION_BUTTON}--text ${CLASS_NAMES.MESSAGE.HIDE_LINK}" data-hide-permanently="true">
96
+ 다시 보지 않기
97
+ </button>
98
+ `;
99
+ }
100
+ renderCloseButton(onClose) {
101
+ if (!onClose) return '';
102
+ return `
103
+ <button type="button" class="${CLASS_NAMES.MESSAGE.CLOSE_BUTTON}" aria-label="알림 닫기">
104
+ ${SVG_ICONS['x-close'](MESSAGE_SIZES.CLOSE_BUTTON).replace('stroke="currentColor"', `stroke="#2F2F30"`)}
105
+ </button>
106
+ `;
107
+ }
108
+ bindEvents() {
109
+ bindNotificationEvents(this.element, this.options.actions, this.options.onClose, () => this.remove());
110
+ // 다시보지 않기 버튼 이벤트 바인딩
111
+ if (this.options.onHidePermanently) {
112
+ const hidePermanentlyButton = this.element.querySelector('[data-hide-permanently="true"]');
113
+ if (hidePermanentlyButton) {
114
+ hidePermanentlyButton.addEventListener('click', () => {
115
+ this.options.onHidePermanently?.();
116
+ this.remove();
117
+ });
118
+ }
119
+ }
120
+ }
121
+ setupAutoClose() {
122
+ this.autoCloseTimer = setupAutoClose(this.options.autoClose, this.options.onClose, () => this.remove());
123
+ }
124
+ // Public methods
125
+ getElement() {
126
+ return this.element;
127
+ }
128
+ appendTo(parent) {
129
+ parent.appendChild(this.element);
130
+ }
131
+ remove() {
132
+ if (this.autoCloseTimer) {
133
+ clearTimeout(this.autoCloseTimer);
134
+ this.autoCloseTimer = undefined;
135
+ }
136
+ if (this.element?.parentNode) {
137
+ this.element.parentNode.removeChild(this.element);
138
+ }
139
+ }
140
+ destroy() {
141
+ // FeaturedIcon 정리
142
+ if (this.featuredIcon) {
143
+ this.featuredIcon.destroy();
144
+ this.featuredIcon = undefined;
145
+ }
146
+ this.remove();
147
+ }
148
+ // Static factory methods
149
+ static create(options) {
150
+ return new MessageNotification(options);
151
+ }
152
+ }