@ncds/ui-admin 1.8.25 → 1.8.26-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/cjs/assets/scripts/notification/FullWidthNotification.js +2 -0
  2. package/dist/cjs/assets/scripts/notification/MessageNotification.js +30 -23
  3. package/dist/cjs/assets/scripts/notification/const/classNames.js +3 -0
  4. package/dist/cjs/assets/scripts/notification/const/icons.js +11 -2
  5. package/dist/cjs/assets/scripts/notification/const/index.js +9 -4
  6. package/dist/cjs/assets/scripts/notification/const/sizes.js +6 -1
  7. package/dist/cjs/assets/scripts/notification/const/types.js +1 -10
  8. package/dist/cjs/assets/scripts/notification/messageTemplate.js +20 -8
  9. package/dist/cjs/assets/scripts/notification/utils.js +21 -17
  10. package/dist/cjs/constant/notification.js +15 -1
  11. package/dist/cjs/src/components/feedback-and-status/badge/Badge.js +1 -1
  12. package/dist/cjs/src/components/forms-and-input/combo-box/ComboBox.js +16 -126
  13. package/dist/cjs/src/components/forms-and-input/combo-box/__tests__/ComboBox.portal.test.js +3 -3
  14. package/dist/cjs/src/components/overlays/notification/FloatingNotification.js +4 -7
  15. package/dist/cjs/src/components/overlays/notification/FullWidthNotification.js +13 -8
  16. package/dist/cjs/src/components/overlays/notification/MessageNotification.js +20 -14
  17. package/dist/cjs/src/components/overlays/notification/MessageNotificationParts.js +22 -15
  18. package/dist/cjs/src/components/overlays/notification/Notification.js +6 -0
  19. package/dist/cjs/src/components/overlays/notification/NotificationActionButtons.js +27 -0
  20. package/dist/cjs/src/components/overlays/notification/__tests__/supportTextLink.test.js +58 -0
  21. package/dist/cjs/src/components/overlays/notification/linkTarget.js +18 -0
  22. package/dist/cjs/src/generated/scoped-css.js +1 -1
  23. package/dist/cjs/src/utils/index.js +11 -0
  24. package/dist/cjs/src/utils/url/__tests__/isSafeUrl.test.js +49 -0
  25. package/dist/cjs/src/utils/url/index.js +16 -0
  26. package/dist/cjs/src/utils/url/isSafeUrl.js +32 -0
  27. package/dist/esm/assets/scripts/notification/FullWidthNotification.js +3 -1
  28. package/dist/esm/assets/scripts/notification/MessageNotification.js +32 -25
  29. package/dist/esm/assets/scripts/notification/const/classNames.js +3 -0
  30. package/dist/esm/assets/scripts/notification/const/icons.js +10 -1
  31. package/dist/esm/assets/scripts/notification/const/index.js +2 -3
  32. package/dist/esm/assets/scripts/notification/const/sizes.js +5 -0
  33. package/dist/esm/assets/scripts/notification/const/types.js +1 -8
  34. package/dist/esm/assets/scripts/notification/messageTemplate.js +21 -9
  35. package/dist/esm/assets/scripts/notification/utils.js +19 -16
  36. package/dist/esm/constant/notification.js +14 -0
  37. package/dist/esm/src/components/feedback-and-status/badge/Badge.js +1 -1
  38. package/dist/esm/src/components/forms-and-input/combo-box/ComboBox.js +17 -127
  39. package/dist/esm/src/components/forms-and-input/combo-box/__tests__/ComboBox.portal.test.js +3 -3
  40. package/dist/esm/src/components/overlays/notification/FloatingNotification.js +4 -7
  41. package/dist/esm/src/components/overlays/notification/FullWidthNotification.js +13 -8
  42. package/dist/esm/src/components/overlays/notification/MessageNotification.js +21 -15
  43. package/dist/esm/src/components/overlays/notification/MessageNotificationParts.js +22 -15
  44. package/dist/esm/src/components/overlays/notification/Notification.js +6 -0
  45. package/dist/esm/src/components/overlays/notification/NotificationActionButtons.js +21 -0
  46. package/dist/esm/src/components/overlays/notification/__tests__/supportTextLink.test.js +55 -0
  47. package/dist/esm/src/components/overlays/notification/linkTarget.js +12 -0
  48. package/dist/esm/src/generated/scoped-css.js +1 -1
  49. package/dist/esm/src/utils/index.js +3 -1
  50. package/dist/esm/src/utils/url/__tests__/isSafeUrl.test.js +47 -0
  51. package/dist/esm/src/utils/url/index.js +1 -0
  52. package/dist/esm/src/utils/url/isSafeUrl.js +26 -0
  53. package/dist/temp/assets/scripts/notification/FullWidthNotification.js +3 -2
  54. package/dist/temp/assets/scripts/notification/MessageNotification.d.ts +2 -0
  55. package/dist/temp/assets/scripts/notification/MessageNotification.js +30 -25
  56. package/dist/temp/assets/scripts/notification/const/classNames.d.ts +3 -0
  57. package/dist/temp/assets/scripts/notification/const/classNames.js +3 -0
  58. package/dist/temp/assets/scripts/notification/const/icons.d.ts +6 -0
  59. package/dist/temp/assets/scripts/notification/const/icons.js +9 -0
  60. package/dist/temp/assets/scripts/notification/const/index.d.ts +3 -4
  61. package/dist/temp/assets/scripts/notification/const/index.js +2 -3
  62. package/dist/temp/assets/scripts/notification/const/sizes.d.ts +4 -0
  63. package/dist/temp/assets/scripts/notification/const/sizes.js +5 -0
  64. package/dist/temp/assets/scripts/notification/const/types.d.ts +28 -1
  65. package/dist/temp/assets/scripts/notification/const/types.js +1 -8
  66. package/dist/temp/assets/scripts/notification/messageTemplate.d.ts +5 -1
  67. package/dist/temp/assets/scripts/notification/messageTemplate.js +16 -11
  68. package/dist/temp/assets/scripts/notification/utils.d.ts +10 -3
  69. package/dist/temp/assets/scripts/notification/utils.js +21 -18
  70. package/dist/temp/constant/notification.d.ts +13 -0
  71. package/dist/temp/constant/notification.js +14 -0
  72. package/dist/temp/src/components/feedback-and-status/badge/Badge.d.ts +1 -1
  73. package/dist/temp/src/components/feedback-and-status/badge/Badge.js +1 -1
  74. package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.d.ts +1 -1
  75. package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.js +13 -119
  76. package/dist/temp/src/components/forms-and-input/combo-box/__tests__/ComboBox.portal.test.js +3 -3
  77. package/dist/temp/src/components/overlays/notification/FloatingNotification.js +2 -2
  78. package/dist/temp/src/components/overlays/notification/FullWidthNotification.d.ts +6 -1
  79. package/dist/temp/src/components/overlays/notification/FullWidthNotification.js +7 -2
  80. package/dist/temp/src/components/overlays/notification/MessageNotification.d.ts +15 -1
  81. package/dist/temp/src/components/overlays/notification/MessageNotification.js +11 -12
  82. package/dist/temp/src/components/overlays/notification/MessageNotificationParts.d.ts +10 -5
  83. package/dist/temp/src/components/overlays/notification/MessageNotificationParts.js +9 -6
  84. package/dist/temp/src/components/overlays/notification/Notification.d.ts +23 -2
  85. package/dist/temp/src/components/overlays/notification/Notification.js +3 -3
  86. package/dist/temp/src/components/overlays/notification/NotificationActionButtons.d.ts +12 -0
  87. package/dist/temp/src/components/overlays/notification/NotificationActionButtons.js +9 -0
  88. package/dist/temp/src/components/overlays/notification/__tests__/supportTextLink.test.js +73 -0
  89. package/dist/temp/src/components/overlays/notification/linkTarget.d.ts +11 -0
  90. package/dist/temp/src/components/overlays/notification/linkTarget.js +9 -0
  91. package/dist/temp/src/generated/scoped-css.js +1 -1
  92. package/dist/temp/src/utils/index.d.ts +1 -0
  93. package/dist/temp/src/utils/index.js +2 -0
  94. package/dist/temp/src/utils/url/__tests__/isSafeUrl.test.js +61 -0
  95. package/dist/temp/src/utils/url/index.d.ts +1 -0
  96. package/dist/temp/src/utils/url/index.js +1 -0
  97. package/dist/temp/src/utils/url/isSafeUrl.d.ts +8 -0
  98. package/dist/temp/src/utils/url/isSafeUrl.js +29 -0
  99. package/dist/types/assets/scripts/notification/MessageNotification.d.ts +2 -0
  100. package/dist/types/assets/scripts/notification/const/classNames.d.ts +3 -0
  101. package/dist/types/assets/scripts/notification/const/icons.d.ts +6 -0
  102. package/dist/types/assets/scripts/notification/const/index.d.ts +3 -4
  103. package/dist/types/assets/scripts/notification/const/sizes.d.ts +4 -0
  104. package/dist/types/assets/scripts/notification/const/types.d.ts +28 -1
  105. package/dist/types/assets/scripts/notification/messageTemplate.d.ts +5 -1
  106. package/dist/types/assets/scripts/notification/utils.d.ts +10 -3
  107. package/dist/types/constant/notification.d.ts +13 -0
  108. package/dist/types/src/components/feedback-and-status/badge/Badge.d.ts +1 -1
  109. package/dist/types/src/components/forms-and-input/combo-box/ComboBox.d.ts +1 -1
  110. package/dist/types/src/components/overlays/notification/FullWidthNotification.d.ts +6 -1
  111. package/dist/types/src/components/overlays/notification/MessageNotification.d.ts +15 -1
  112. package/dist/types/src/components/overlays/notification/MessageNotificationParts.d.ts +10 -5
  113. package/dist/types/src/components/overlays/notification/Notification.d.ts +23 -2
  114. package/dist/types/src/components/overlays/notification/NotificationActionButtons.d.ts +12 -0
  115. package/dist/types/src/components/overlays/notification/__tests__/supportTextLink.test.d.ts +1 -0
  116. package/dist/types/src/components/overlays/notification/linkTarget.d.ts +11 -0
  117. package/dist/types/src/utils/index.d.ts +1 -0
  118. package/dist/types/src/utils/url/__tests__/isSafeUrl.test.d.ts +1 -0
  119. package/dist/types/src/utils/url/index.d.ts +1 -0
  120. package/dist/types/src/utils/url/isSafeUrl.d.ts +8 -0
  121. package/dist/ui-admin/assets/styles/style.css +54 -34
  122. package/package.json +1 -1
  123. package/dist/cjs/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.js +0 -455
  124. package/dist/esm/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.js +0 -452
  125. package/dist/temp/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.js +0 -293
  126. /package/dist/temp/src/components/{forms-and-input/combo-box/__tests__/ComboBox.test.d.ts → overlays/notification/__tests__/supportTextLink.test.d.ts} +0 -0
  127. /package/dist/{types/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.d.ts → temp/src/utils/url/__tests__/isSafeUrl.test.d.ts} +0 -0
@@ -1,2 +1,3 @@
1
1
  export * from './date-picker';
2
2
  export * from './dropdown';
3
+ export * from './url';
@@ -2,3 +2,5 @@
2
2
  export * from './date-picker';
3
3
  // 드롭다운 관련 utils
4
4
  export * from './dropdown';
5
+ // URL 관련 utils
6
+ export * from './url';
@@ -0,0 +1,61 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { isSafeUrl } from '../isSafeUrl';
3
+ /**
4
+ * 알림 텍스트 링크(`supportTextLink`)의 href 허용 여부.
5
+ * React·Vanilla 가 이 함수 하나를 공유한다 (#538 리뷰 H2).
6
+ *
7
+ * 이 파일은 vitest 기본 환경(node)에서 돈다 — `window` 가 없는 SSR 상황 자체가 검증 대상이다.
8
+ */
9
+ const SAFE = [
10
+ 'https://example.com/notice/1',
11
+ 'http://example.com',
12
+ 'HTTPS://EXAMPLE.COM',
13
+ '/notice/1',
14
+ 'notice/1',
15
+ '../notice',
16
+ '?page=2',
17
+ '#section',
18
+ ' https://example.com ',
19
+ ];
20
+ const UNSAFE = [
21
+ 'javascript:alert(1)',
22
+ 'JavaScript:alert(1)',
23
+ ' JavaScript:alert(1)',
24
+ '\tjavascript:alert(1)',
25
+ 'java\nscript:alert(1)',
26
+ 'data:text/html,<script>alert(1)</script>',
27
+ 'DATA:text/html;base64,PHNjcmlwdD4=',
28
+ 'vbscript:msgbox(1)',
29
+ 'file:///etc/passwd',
30
+ 'mailto:a@example.com',
31
+ 'http://',
32
+ ];
33
+ describe('isSafeUrl — window 없음(SSR)', () => {
34
+ it('window 가 정의되지 않은 환경이다', () => {
35
+ expect(typeof window).toBe('undefined');
36
+ });
37
+ it.each(SAFE)('허용: %j', (url) => {
38
+ expect(isSafeUrl(url)).toBe(true);
39
+ });
40
+ it.each(UNSAFE)('차단: %j', (url) => {
41
+ expect(isSafeUrl(url)).toBe(false);
42
+ });
43
+ it('빈 문자열은 차단한다', () => {
44
+ expect(isSafeUrl('')).toBe(false);
45
+ });
46
+ });
47
+ describe('isSafeUrl — window 존재', () => {
48
+ afterEach(() => {
49
+ vi.unstubAllGlobals();
50
+ });
51
+ it('현재 origin 기준으로 상대경로를 해석해 허용한다', () => {
52
+ vi.stubGlobal('window', { location: { origin: 'https://admin.example.com' } });
53
+ expect(isSafeUrl('/notice/1')).toBe(true);
54
+ expect(isSafeUrl('javascript:alert(1)')).toBe(false);
55
+ });
56
+ it('origin 이 "null"(about:blank·sandbox iframe)이어도 상대경로를 허용한다', () => {
57
+ vi.stubGlobal('window', { location: { origin: 'null' } });
58
+ expect(isSafeUrl('/notice/1')).toBe(true);
59
+ expect(isSafeUrl(' JavaScript:alert(1)')).toBe(false);
60
+ });
61
+ });
@@ -0,0 +1 @@
1
+ export * from './isSafeUrl';
@@ -0,0 +1 @@
1
+ export * from './isSafeUrl';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 링크 href 로 넣어도 안전한 URL 인지 판정한다. http/https 및 상대경로만 허용한다.
3
+ * javascript:, data:, file: 등 위험 프로토콜로 인한 XSS / 외부 페이로드 로딩을 차단.
4
+ *
5
+ * URL 파서가 앞뒤 공백·탭·개행 제거와 scheme 소문자화를 하므로 ` JavaScript:` 같은 우회도 막힌다.
6
+ * React(`MessageNotificationParts`·`FullWidthNotification`)와 Vanilla(`renderSupportingText`)가 공유한다.
7
+ */
8
+ export declare function isSafeUrl(url: string): boolean;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 상대경로 해석용 기본 origin. `window` 가 없거나(SSR) origin 이 불투명(`"null"` — about:blank,
3
+ * sandbox iframe)일 때 쓴다. 판정은 프로토콜만 보므로 실제 호스트와 무관하다.
4
+ */
5
+ const FALLBACK_ORIGIN = 'http://localhost';
6
+ function resolveBaseOrigin() {
7
+ if (typeof window === 'undefined')
8
+ return FALLBACK_ORIGIN;
9
+ const origin = window.location?.origin;
10
+ return origin && origin !== 'null' ? origin : FALLBACK_ORIGIN;
11
+ }
12
+ /**
13
+ * 링크 href 로 넣어도 안전한 URL 인지 판정한다. http/https 및 상대경로만 허용한다.
14
+ * javascript:, data:, file: 등 위험 프로토콜로 인한 XSS / 외부 페이로드 로딩을 차단.
15
+ *
16
+ * URL 파서가 앞뒤 공백·탭·개행 제거와 scheme 소문자화를 하므로 ` JavaScript:` 같은 우회도 막힌다.
17
+ * React(`MessageNotificationParts`·`FullWidthNotification`)와 Vanilla(`renderSupportingText`)가 공유한다.
18
+ */
19
+ export function isSafeUrl(url) {
20
+ if (!url.trim())
21
+ return false;
22
+ try {
23
+ const parsed = new URL(url, resolveBaseOrigin());
24
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
@@ -9,6 +9,8 @@ export declare class MessageNotification {
9
9
  private featuredIcon?;
10
10
  constructor(options: MessageNotificationOptions);
11
11
  private createElement;
12
+ /** 색상별 FeaturedIcon. default 는 md(40/20), block 은 sm(32/16). */
13
+ private createFeaturedIcon;
12
14
  private bindEvents;
13
15
  private setupAutoClose;
14
16
  getElement(): HTMLElement;
@@ -8,6 +8,7 @@ export declare const CLASS_NAMES: {
8
8
  readonly TEXT_CONTAINER: "ncua-full-width-notification__text-container";
9
9
  readonly TITLE: "ncua-full-width-notification__title";
10
10
  readonly SUPPORTING_TEXT: "ncua-full-width-notification__supporting-text";
11
+ readonly LINK: "ncua-full-width-notification__link";
11
12
  readonly ACTIONS_CONTAINER: "ncua-full-width-notification__actions-container";
12
13
  readonly ACTIONS: "ncua-full-width-notification__actions";
13
14
  readonly CLOSE_BUTTON: "ncua-full-width-notification__close-button";
@@ -25,6 +26,7 @@ export declare const CLASS_NAMES: {
25
26
  };
26
27
  readonly MESSAGE: {
27
28
  readonly BASE: "ncua-message-notification";
29
+ readonly BLOCK: "ncua-message-notification--block";
28
30
  readonly CONTAINER: "ncua-message-notification__container";
29
31
  readonly CONTENT: "ncua-message-notification__content";
30
32
  readonly CONTENT_WRAPPER: "ncua-message-notification__content-wrapper";
@@ -32,6 +34,7 @@ export declare const CLASS_NAMES: {
32
34
  readonly TEXT_CONTAINER: "ncua-message-notification__text-container";
33
35
  readonly TITLE: "ncua-message-notification__title";
34
36
  readonly SUPPORTING_TEXT: "ncua-message-notification__supporting-text";
37
+ readonly LINK: "ncua-message-notification__link";
35
38
  readonly ACTIONS_CONTAINER: "ncua-message-notification__actions-container";
36
39
  readonly ACTIONS: "ncua-message-notification__actions";
37
40
  readonly FOOTER_CONTAINER: "ncua-message-notification__footer-container";
@@ -23,3 +23,9 @@ export declare const ICON_MAP: {
23
23
  readonly success: (size: string) => string;
24
24
  readonly info: (size: string) => string;
25
25
  };
26
+ /**
27
+ * message 타입 아이콘. floating 과 같은 4색에 block 변형 전용 info 를 더한다.
28
+ * `SVG_ICONS['info-circle']`은 full-width 전용 class·color 가 박혀 있어 쓰지 않는다
29
+ * (`.ncua-full-width-notification__icon`은 모바일에서 숨겨진다).
30
+ */
31
+ export declare const MESSAGE_ICON_MAP: Partial<Record<NotificationColor, (size: string) => string>>;
@@ -1,5 +1,4 @@
1
1
  export { CLASS_NAMES } from './classNames';
2
- export { FLOATING_ICON_MAP, FULL_WIDTH_ICON_MAP, ICON_MAP, SVG_ICONS } from './icons';
3
- export { CLOSE_BUTTON_SIZES, CLOSE_BUTTON_SVG_SIZES, FEATURED_ICON_SIZES, FULL_WIDTH_SIZES, getSizes, ICON_PIXEL_SIZES, MESSAGE_SIZES, } from './sizes';
4
- export type { BaseNotificationOptions, LegacyNotificationHierarchy, NotificationAction, NotificationActionButtonSize, NotificationColor, NotificationHierarchy, } from './types';
5
- export { MESSAGE_CLOSE_ICON_COLORS } from './types';
2
+ export { FLOATING_ICON_MAP, FULL_WIDTH_ICON_MAP, ICON_MAP, MESSAGE_ICON_MAP, SVG_ICONS } from './icons';
3
+ export { CLOSE_BUTTON_SIZES, CLOSE_BUTTON_SVG_SIZES, FEATURED_ICON_SIZES, FULL_WIDTH_SIZES, getSizes, ICON_PIXEL_SIZES, MESSAGE_BLOCK_SIZES, MESSAGE_SIZES, } from './sizes';
4
+ export type { BaseNotificationOptions, LegacyNotificationHierarchy, NotificationAction, NotificationActionButtonSize, NotificationColor, NotificationHierarchy, NotificationLinkTarget, NotificationVariant, } from './types';
@@ -24,6 +24,10 @@ export declare const MESSAGE_SIZES: {
24
24
  readonly ICON_PIXEL: "20";
25
25
  readonly CLOSE_BUTTON: "20";
26
26
  };
27
+ export declare const MESSAGE_BLOCK_SIZES: {
28
+ readonly FEATURED_ICON: "sm";
29
+ readonly ICON_PIXEL: "16";
30
+ };
27
31
  export declare const getSizes: {
28
32
  readonly featuredIcon: (isMobile: boolean) => "sm" | "md";
29
33
  readonly iconPixel: (isMobile: boolean) => "20" | "16";
@@ -1,4 +1,16 @@
1
1
  export type NotificationColor = 'neutral' | 'error' | 'warning' | 'success' | 'info';
2
+ /**
3
+ * message 타입의 표시 변형. React `NotificationVariant`와 같은 어휘다.
4
+ * - `'default'`: 기존 message
5
+ * - `'block'`: 서브 페이지 공지용 (흰 배경, FeaturedIcon sm, 제목 + 텍스트 링크 1행, info 유지)
6
+ */
7
+ export type NotificationVariant = 'default' | 'block';
8
+ /**
9
+ * 텍스트 링크(`supportTextLink`)를 여는 대상. React `NotificationLinkTarget`과 같은 어휘다.
10
+ * - `'_self'`: 같은 탭 (target·rel 미부여)
11
+ * - `'_blank'`: 새 탭 (`target="_blank"` + `rel="noopener noreferrer"`)
12
+ */
13
+ export type NotificationLinkTarget = '_self' | '_blank';
2
14
  /**
3
15
  * 알림 액션 버튼(`ncua-btn`)의 색상 수식자.
4
16
  * React `NotificationAction.hierarchy`와 동일한 어휘를 쓴다 — `ButtonTheme`의 부분집합이다.
@@ -31,7 +43,23 @@ export interface BaseNotificationOptions {
31
43
  className?: string;
32
44
  actions?: NotificationAction[];
33
45
  autoClose?: number;
46
+ /**
47
+ * supportingText에 대한 링크 URL. full-width 타입, message 타입의 `variant: 'block'`에서 사용 가능.
48
+ * http/https 만 허용되며 그 외 프로토콜은 링크 없이 텍스트만 렌더된다.
49
+ */
34
50
  supportTextLink?: string;
51
+ /**
52
+ * 텍스트 링크를 여는 대상. `supportTextLink`가 렌더될 때만 동작한다.
53
+ * `'_blank'`이면 새 탭으로 열고 `rel="noopener noreferrer"`를 함께 붙인다.
54
+ * `'_blank'`가 아닌 값은 모두 `'_self'`(같은 탭)로 취급한다.
55
+ * @default '_self'
56
+ */
57
+ supportTextLinkTarget?: NotificationLinkTarget;
58
+ /**
59
+ * message 타입의 표시 변형. message 타입에서만 동작한다.
60
+ * @default 'default'
61
+ */
62
+ variant?: NotificationVariant;
35
63
  onHidePermanently?: () => void;
36
64
  /**
37
65
  * 다시보지 않기 버튼의 라벨. full-width, message 타입에서 사용 가능하며
@@ -44,4 +72,3 @@ export interface BaseNotificationOptions {
44
72
  */
45
73
  hidePermanentlyLabel?: string;
46
74
  }
47
- export declare const MESSAGE_CLOSE_ICON_COLORS: Record<NotificationColor, string>;
@@ -1,7 +1,11 @@
1
- import type { NotificationAction } from './const';
1
+ import type { NotificationAction, NotificationColor } from './const';
2
2
  export interface MessageTemplateParams {
3
3
  title: string;
4
4
  supportingText?: string;
5
+ /** 지정 시 `supportingText`를 텍스트 링크로 렌더한다 (block 변형 전용, http/https 만 허용). */
6
+ supportTextLink?: string;
7
+ /** 닫기 아이콘 색 결정용. 미지정 시 neutral. */
8
+ color?: NotificationColor;
5
9
  actions?: NotificationAction[];
6
10
  onClose?: () => void;
7
11
  onHidePermanently?: () => void;
@@ -1,7 +1,15 @@
1
1
  import type { LegacyNotificationHierarchy, NotificationAction, NotificationActionButtonSize, NotificationColor, NotificationHierarchy } from './const';
2
2
  export declare function createWrapperElement(baseClass: string, color: NotificationColor, className?: string): HTMLElement;
3
3
  export declare function buildClassName(baseClass: string, color: NotificationColor, className?: string): string;
4
- export declare function renderSupportingText(supportingText?: string, className?: string, supportTextLink?: string): string;
4
+ export declare function renderSupportingText(supportingText?: string, className?: string, supportTextLink?: string, linkClassName?: string): string;
5
+ /**
6
+ * `supportTextLinkTarget`이 `'_blank'`이면 `renderSupportingText`가 만든 링크를 새 탭으로 연다.
7
+ *
8
+ * target 은 sanitize **이후** 우리가 만든 링크(텍스트 컨테이너의 직계 `<a>`)에만 지정한다.
9
+ * DOMPurify 에 target 을 전역 허용하면 사용자 입력 HTML 의 `<a target>`까지 rel 없이 통과하기 때문이다.
10
+ * 런타임 입력이므로 `'_blank'`가 아닌 값은 전부 `'_self'`(속성 미부여)로 취급한다.
11
+ */
12
+ export declare function applySupportTextLinkTarget(root: HTMLElement, textContainerClassName: string, linkClassName: string, target: unknown): void;
5
13
  /**
6
14
  * 액션 버튼의 색상 수식자를 현행 어휘로 정규화한다.
7
15
  *
@@ -16,8 +24,7 @@ export declare function resolveActionHierarchy(hierarchy?: NotificationHierarchy
16
24
  /**
17
25
  * 액션 버튼 그룹 HTML을 만든다.
18
26
  *
19
- * `size` 기본값 'sm'은 floating / full-width 타입의 기존 렌더링을 그대로 보존하기 위한 것이다.
20
- * message 타입만 TO-BE 규격(28px)에 맞춰 'xs'를 넘긴다 — React `<Button size="xs">`와 동일.
27
+ * `size` 기본값 'xs'는 디자인 기준(#535 — 모든 type 액션 버튼 xs)이며 React `<Button size="xs">`와 같다.
21
28
  */
22
29
  export declare function renderActions(actions: NotificationAction[], wrapperClass: string, size?: NotificationActionButtonSize): string;
23
30
  export declare function bindNotificationEvents(element: HTMLElement, actions: NotificationAction[], onClose?: () => void, onRemove?: () => void): void;
@@ -13,3 +13,16 @@ export declare const HIDE_PERMANENTLY_LABEL: {
13
13
  readonly message: "다시 보지 않기";
14
14
  readonly fullWidth: "다시보지 않기";
15
15
  };
16
+ /**
17
+ * message 타입 닫기(X) 아이콘의 color 별 색.
18
+ *
19
+ * React(`MessageNotification.tsx`)와 Vanilla(`messageTemplate.ts`)가 함께 참조하는 단일 출처다.
20
+ * `info`는 `variant: 'block'`에서만 렌더된다(default 는 neutral 로 폴백).
21
+ */
22
+ export declare const MESSAGE_CLOSE_ICON_COLOR: {
23
+ readonly neutral: string;
24
+ readonly error: string;
25
+ readonly warning: string;
26
+ readonly success: string;
27
+ readonly info: string;
28
+ };
@@ -16,4 +16,4 @@ type BadgeProps = {
16
16
  };
17
17
  declare const Badge: ({ label, type, color, className, leadingIcon, trailingIcon, size, }: BadgeProps) => import("react/jsx-runtime").JSX.Element;
18
18
  export { Badge };
19
- export type { BadgeType, BadgeColor, BadgeSize, BadgeProps };
19
+ export type { BadgeColor, BadgeProps, BadgeSize, BadgeType };
@@ -10,7 +10,7 @@ interface ComboBoxProps extends Omit<ComponentPropsWithRef<'div'>, 'size' | 'onC
10
10
  destructive?: boolean;
11
11
  size?: Extract<Size, 'xs' | 'sm'>;
12
12
  optionItems?: OptionType[];
13
- value?: OptionValue | null;
13
+ value?: OptionValue;
14
14
  onChange?: OptionChangeHandler;
15
15
  onSearch?: (searchValue: string) => void;
16
16
  disabled?: boolean;
@@ -1,5 +1,5 @@
1
1
  import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
2
- import type { NotificationAction, NotificationColor } from './Notification';
2
+ import type { NotificationAction, NotificationColor, NotificationLinkTarget } from './Notification';
3
3
  interface FullWidthNotificationProps extends Omit<ComponentPropsWithoutRef<'div'>, 'title'> {
4
4
  /**
5
5
  * 알림 제목 텍스트
@@ -36,6 +36,11 @@ interface FullWidthNotificationProps extends Omit<ComponentPropsWithoutRef<'div'
36
36
  * info 링크 영역 (선택사항)
37
37
  */
38
38
  supportTextLink?: string;
39
+ /**
40
+ * 텍스트 링크를 여는 대상 (선택사항). `'_blank'`이면 새 탭 + `rel="noopener noreferrer"`
41
+ * @default '_self'
42
+ */
43
+ supportTextLinkTarget?: NotificationLinkTarget;
39
44
  /**
40
45
  * 다시보지 않기 이벤트 핸들러 (선택사항)
41
46
  */
@@ -1,6 +1,6 @@
1
1
  import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
2
2
  import type { SlotIconComponent } from '../../../types/side-slot';
3
- import type { NotificationAction, NotificationColor } from './Notification';
3
+ import type { NotificationAction, NotificationColor, NotificationLinkTarget, NotificationVariant } from './Notification';
4
4
  interface MessageNotificationProps extends Omit<ComponentPropsWithoutRef<'div'>, 'title'> {
5
5
  /**
6
6
  * 알림 제목 텍스트
@@ -41,6 +41,20 @@ interface MessageNotificationProps extends Omit<ComponentPropsWithoutRef<'div'>,
41
41
  * @default '다시 보지 않기'
42
42
  */
43
43
  hidePermanentlyLabel?: ReactNode;
44
+ /**
45
+ * 표시 변형. `'block'`은 서브 페이지 공지용(흰 배경, FeaturedIcon sm, 제목 + 텍스트 링크 1행, info 유지)
46
+ * @default 'default'
47
+ */
48
+ variant?: NotificationVariant;
49
+ /**
50
+ * 텍스트 링크 URL. `variant="block"`에서만 동작하며 `supportingText`가 링크 텍스트가 된다.
51
+ */
52
+ supportTextLink?: string;
53
+ /**
54
+ * 텍스트 링크를 여는 대상. `'_blank'`이면 새 탭 + `rel="noopener noreferrer"`
55
+ * @default '_self'
56
+ */
57
+ supportTextLinkTarget?: NotificationLinkTarget;
44
58
  }
45
59
  declare const MessageNotification: import("react").ForwardRefExoticComponent<MessageNotificationProps & import("react").RefAttributes<HTMLDivElement>>;
46
60
  export type { MessageNotificationProps };
@@ -1,16 +1,21 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { SlotIconComponent } from '../../../types/side-slot';
3
- import { type FeaturedIconColor } from '../../image-and-icons/featured-icon/FeaturedIcon';
4
- import type { NotificationAction } from './Notification';
3
+ import { type FeaturedIconColor, type FeaturedIconSize } from '../../image-and-icons/featured-icon/FeaturedIcon';
4
+ import type { NotificationAction, NotificationLinkTarget } from './Notification';
5
5
  interface MessageNotificationContentProps {
6
6
  title: ReactNode;
7
7
  supportingText?: ReactNode;
8
+ /** 지정 시 `supportingText`를 이 URL 의 텍스트 링크로 렌더한다 (block 변형 전용). http/https·상대경로가 아니면 텍스트만 렌더. */
9
+ supportTextLink?: string;
10
+ /** 텍스트 링크를 여는 대상. `'_blank'`이면 새 탭 + `rel="noopener noreferrer"`. */
11
+ supportTextLinkTarget?: NotificationLinkTarget;
8
12
  icon?: SlotIconComponent;
9
13
  iconColor: FeaturedIconColor;
10
- showIcon: boolean;
14
+ /** default 는 md(40/20), block 은 sm(32/16). */
15
+ iconSize: FeaturedIconSize;
11
16
  }
12
- /** 아이콘 + 제목/서포팅 텍스트 영역. */
13
- declare const MessageNotificationContent: ({ title, supportingText, icon, iconColor, showIcon, }: MessageNotificationContentProps) => import("react/jsx-runtime").JSX.Element;
17
+ /** 아이콘 + 제목/서포팅 텍스트(또는 텍스트 링크) 영역. */
18
+ declare const MessageNotificationContent: ({ title, supportingText, supportTextLink, supportTextLinkTarget, icon, iconColor, iconSize, }: MessageNotificationContentProps) => import("react/jsx-runtime").JSX.Element;
14
19
  interface MessageNotificationActionsProps {
15
20
  actions?: NotificationAction[];
16
21
  onClose?: () => void;
@@ -6,6 +6,9 @@ import { type CalloutNotificationProps, type CalloutNotificationWidth } from './
6
6
  type NotificationType = 'floating' | 'full-width' | 'message' | 'callout';
7
7
  type NotificationColor = Extract<ColorTone, 'neutral' | 'error' | 'warning' | 'success' | 'info'>;
8
8
  type NotificationSize = 'desktop' | 'mobile';
9
+ type NotificationVariant = 'default' | 'block';
10
+ /** 텍스트 링크(`supportTextLink`)를 여는 대상. `'_blank'`이면 새 탭 + `rel="noopener noreferrer"`. */
11
+ type NotificationLinkTarget = '_self' | '_blank';
9
12
  interface NotificationAction {
10
13
  /**
11
14
  * 액션 버튼 텍스트
@@ -78,9 +81,27 @@ interface NotificationProps extends Omit<ComponentPropsWithoutRef<'div'>, 'title
78
81
  hidePermanentlyLabel?: ReactNode;
79
82
  /**
80
83
  * supportingText 에 연결할 링크 URL (선택사항)
81
- * full-width 타입에서 사용 가능
84
+ * full-width 타입, message 타입의 `variant="block"`에서 사용 가능
82
85
  */
83
86
  supportTextLink?: string;
87
+ /**
88
+ * 텍스트 링크를 여는 대상 (선택사항). `supportTextLink`가 렌더될 때만 동작한다.
89
+ * `'_blank'`이면 새 탭으로 열고 `rel="noopener noreferrer"`를 함께 붙인다.
90
+ * full-width 타입, message 타입의 `variant="block"`에서 사용 가능
91
+ * @default '_self'
92
+ */
93
+ supportTextLinkTarget?: NotificationLinkTarget;
94
+ /**
95
+ * message 타입의 표시 변형 (선택사항). message 타입에서만 동작한다.
96
+ *
97
+ * - `'default'`: 기존 message (color 별 배경·테두리, 제목 + 본문 2행, info → neutral 폴백)
98
+ * - `'block'`: 서브 페이지 공지용. 흰 배경·gray-100 테두리·shadow-sm, FeaturedIcon sm,
99
+ * 제목 + 텍스트 링크 1행(`supportingText` 가 링크 텍스트, `supportTextLink` 가 href).
100
+ * `color="info"` 를 그대로 렌더한다.
101
+ *
102
+ * @default 'default'
103
+ */
104
+ variant?: NotificationVariant;
84
105
  /**
85
106
  * 너비 동작 (선택사항)
86
107
  * callout 타입에서만 사용 가능. 미지정 시 부모 너비를 채운다(100%). 'fit' 은 콘텐츠 너비에 맞춘다
@@ -101,6 +122,6 @@ interface NotificationProps extends Omit<ComponentPropsWithoutRef<'div'>, 'title
101
122
  portal?: boolean;
102
123
  }
103
124
  declare const Notification: import("react").ForwardRefExoticComponent<NotificationProps & import("react").RefAttributes<HTMLDivElement>>;
104
- export type { NotificationType, NotificationColor, NotificationSize, NotificationAction, NotificationProps };
125
+ export type { NotificationType, NotificationColor, NotificationSize, NotificationVariant, NotificationLinkTarget, NotificationAction, NotificationProps, };
105
126
  export type { CalloutNotificationProps, CalloutNotificationWidth };
106
127
  export { Notification };
@@ -0,0 +1,12 @@
1
+ import type { NotificationAction } from './Notification';
2
+ interface NotificationActionButtonsProps {
3
+ actions: NotificationAction[];
4
+ }
5
+ /**
6
+ * 알림 액션 버튼 목록. 모든 type 이 같은 규격(`ncua-btn--xs` + hierarchy, 기본 `text`)을 쓴다(#535).
7
+ * Vanilla `renderActions`(assets/scripts/notification/utils.ts)와 같은 클래스를 산출해야 한다.
8
+ * 감싸는 래퍼(`__actions`)는 type 마다 클래스가 달라 호출부가 담당한다.
9
+ */
10
+ declare const NotificationActionButtons: ({ actions }: NotificationActionButtonsProps) => import("react/jsx-runtime").JSX.Element;
11
+ export type { NotificationActionButtonsProps };
12
+ export { NotificationActionButtons };
@@ -0,0 +1,11 @@
1
+ import type { NotificationLinkTarget } from './Notification';
2
+ /**
3
+ * 텍스트 링크(`supportTextLink`)의 `<a>`에 붙일 target·rel 속성.
4
+ *
5
+ * `'_blank'`일 때만 새 탭 + `rel="noopener noreferrer"`를 반환하고, `'_self'`(기본)는 아무 속성도 붙이지 않는다.
6
+ * Vanilla `applySupportTextLinkTarget`과 같은 마크업을 산출해야 한다.
7
+ */
8
+ export declare function getLinkTargetAttributes(target?: NotificationLinkTarget): {
9
+ readonly target: "_blank";
10
+ readonly rel: "noopener noreferrer";
11
+ } | undefined;
@@ -1,2 +1,3 @@
1
1
  export * from './date-picker';
2
2
  export * from './dropdown';
3
+ export * from './url';
@@ -0,0 +1 @@
1
+ export * from './isSafeUrl';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 링크 href 로 넣어도 안전한 URL 인지 판정한다. http/https 및 상대경로만 허용한다.
3
+ * javascript:, data:, file: 등 위험 프로토콜로 인한 XSS / 외부 페이로드 로딩을 차단.
4
+ *
5
+ * URL 파서가 앞뒤 공백·탭·개행 제거와 scheme 소문자화를 하므로 ` JavaScript:` 같은 우회도 막힌다.
6
+ * React(`MessageNotificationParts`·`FullWidthNotification`)와 Vanilla(`renderSupportingText`)가 공유한다.
7
+ */
8
+ export declare function isSafeUrl(url: string): boolean;