@ncds/ui-admin 1.8.25 β†’ 1.8.26-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) 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/overlays/notification/FloatingNotification.js +4 -7
  13. package/dist/cjs/src/components/overlays/notification/FullWidthNotification.js +13 -8
  14. package/dist/cjs/src/components/overlays/notification/MessageNotification.js +20 -14
  15. package/dist/cjs/src/components/overlays/notification/MessageNotificationParts.js +22 -15
  16. package/dist/cjs/src/components/overlays/notification/Notification.js +6 -0
  17. package/dist/cjs/src/components/overlays/notification/NotificationActionButtons.js +27 -0
  18. package/dist/cjs/src/components/overlays/notification/__tests__/supportTextLink.test.js +58 -0
  19. package/dist/cjs/src/components/overlays/notification/linkTarget.js +18 -0
  20. package/dist/cjs/src/generated/scoped-css.js +1 -1
  21. package/dist/cjs/src/utils/index.js +11 -0
  22. package/dist/cjs/src/utils/url/__tests__/isSafeUrl.test.js +49 -0
  23. package/dist/cjs/src/utils/url/index.js +16 -0
  24. package/dist/cjs/src/utils/url/isSafeUrl.js +32 -0
  25. package/dist/esm/assets/scripts/notification/FullWidthNotification.js +3 -1
  26. package/dist/esm/assets/scripts/notification/MessageNotification.js +32 -25
  27. package/dist/esm/assets/scripts/notification/const/classNames.js +3 -0
  28. package/dist/esm/assets/scripts/notification/const/icons.js +10 -1
  29. package/dist/esm/assets/scripts/notification/const/index.js +2 -3
  30. package/dist/esm/assets/scripts/notification/const/sizes.js +5 -0
  31. package/dist/esm/assets/scripts/notification/const/types.js +1 -8
  32. package/dist/esm/assets/scripts/notification/messageTemplate.js +21 -9
  33. package/dist/esm/assets/scripts/notification/utils.js +19 -16
  34. package/dist/esm/constant/notification.js +14 -0
  35. package/dist/esm/src/components/feedback-and-status/badge/Badge.js +1 -1
  36. package/dist/esm/src/components/overlays/notification/FloatingNotification.js +4 -7
  37. package/dist/esm/src/components/overlays/notification/FullWidthNotification.js +13 -8
  38. package/dist/esm/src/components/overlays/notification/MessageNotification.js +21 -15
  39. package/dist/esm/src/components/overlays/notification/MessageNotificationParts.js +22 -15
  40. package/dist/esm/src/components/overlays/notification/Notification.js +6 -0
  41. package/dist/esm/src/components/overlays/notification/NotificationActionButtons.js +21 -0
  42. package/dist/esm/src/components/overlays/notification/__tests__/supportTextLink.test.js +55 -0
  43. package/dist/esm/src/components/overlays/notification/linkTarget.js +12 -0
  44. package/dist/esm/src/generated/scoped-css.js +1 -1
  45. package/dist/esm/src/utils/index.js +3 -1
  46. package/dist/esm/src/utils/url/__tests__/isSafeUrl.test.js +47 -0
  47. package/dist/esm/src/utils/url/index.js +1 -0
  48. package/dist/esm/src/utils/url/isSafeUrl.js +26 -0
  49. package/dist/temp/assets/scripts/notification/FullWidthNotification.js +3 -2
  50. package/dist/temp/assets/scripts/notification/MessageNotification.d.ts +2 -0
  51. package/dist/temp/assets/scripts/notification/MessageNotification.js +30 -25
  52. package/dist/temp/assets/scripts/notification/const/classNames.d.ts +3 -0
  53. package/dist/temp/assets/scripts/notification/const/classNames.js +3 -0
  54. package/dist/temp/assets/scripts/notification/const/icons.d.ts +6 -0
  55. package/dist/temp/assets/scripts/notification/const/icons.js +9 -0
  56. package/dist/temp/assets/scripts/notification/const/index.d.ts +3 -4
  57. package/dist/temp/assets/scripts/notification/const/index.js +2 -3
  58. package/dist/temp/assets/scripts/notification/const/sizes.d.ts +4 -0
  59. package/dist/temp/assets/scripts/notification/const/sizes.js +5 -0
  60. package/dist/temp/assets/scripts/notification/const/types.d.ts +28 -1
  61. package/dist/temp/assets/scripts/notification/const/types.js +1 -8
  62. package/dist/temp/assets/scripts/notification/messageTemplate.d.ts +5 -1
  63. package/dist/temp/assets/scripts/notification/messageTemplate.js +16 -11
  64. package/dist/temp/assets/scripts/notification/utils.d.ts +10 -3
  65. package/dist/temp/assets/scripts/notification/utils.js +21 -18
  66. package/dist/temp/constant/notification.d.ts +13 -0
  67. package/dist/temp/constant/notification.js +14 -0
  68. package/dist/temp/src/components/feedback-and-status/badge/Badge.d.ts +1 -1
  69. package/dist/temp/src/components/feedback-and-status/badge/Badge.js +1 -1
  70. package/dist/temp/src/components/overlays/notification/FloatingNotification.js +2 -2
  71. package/dist/temp/src/components/overlays/notification/FullWidthNotification.d.ts +6 -1
  72. package/dist/temp/src/components/overlays/notification/FullWidthNotification.js +7 -2
  73. package/dist/temp/src/components/overlays/notification/MessageNotification.d.ts +15 -1
  74. package/dist/temp/src/components/overlays/notification/MessageNotification.js +11 -12
  75. package/dist/temp/src/components/overlays/notification/MessageNotificationParts.d.ts +10 -5
  76. package/dist/temp/src/components/overlays/notification/MessageNotificationParts.js +9 -6
  77. package/dist/temp/src/components/overlays/notification/Notification.d.ts +23 -2
  78. package/dist/temp/src/components/overlays/notification/Notification.js +3 -3
  79. package/dist/temp/src/components/overlays/notification/NotificationActionButtons.d.ts +12 -0
  80. package/dist/temp/src/components/overlays/notification/NotificationActionButtons.js +9 -0
  81. package/dist/temp/src/components/overlays/notification/__tests__/supportTextLink.test.d.ts +1 -0
  82. package/dist/temp/src/components/overlays/notification/__tests__/supportTextLink.test.js +73 -0
  83. package/dist/temp/src/components/overlays/notification/linkTarget.d.ts +11 -0
  84. package/dist/temp/src/components/overlays/notification/linkTarget.js +9 -0
  85. package/dist/temp/src/generated/scoped-css.js +1 -1
  86. package/dist/temp/src/utils/index.d.ts +1 -0
  87. package/dist/temp/src/utils/index.js +2 -0
  88. package/dist/temp/src/utils/url/__tests__/isSafeUrl.test.d.ts +1 -0
  89. package/dist/temp/src/utils/url/__tests__/isSafeUrl.test.js +61 -0
  90. package/dist/temp/src/utils/url/index.d.ts +1 -0
  91. package/dist/temp/src/utils/url/index.js +1 -0
  92. package/dist/temp/src/utils/url/isSafeUrl.d.ts +8 -0
  93. package/dist/temp/src/utils/url/isSafeUrl.js +29 -0
  94. package/dist/types/assets/scripts/notification/MessageNotification.d.ts +2 -0
  95. package/dist/types/assets/scripts/notification/const/classNames.d.ts +3 -0
  96. package/dist/types/assets/scripts/notification/const/icons.d.ts +6 -0
  97. package/dist/types/assets/scripts/notification/const/index.d.ts +3 -4
  98. package/dist/types/assets/scripts/notification/const/sizes.d.ts +4 -0
  99. package/dist/types/assets/scripts/notification/const/types.d.ts +28 -1
  100. package/dist/types/assets/scripts/notification/messageTemplate.d.ts +5 -1
  101. package/dist/types/assets/scripts/notification/utils.d.ts +10 -3
  102. package/dist/types/constant/notification.d.ts +13 -0
  103. package/dist/types/src/components/feedback-and-status/badge/Badge.d.ts +1 -1
  104. package/dist/types/src/components/overlays/notification/FullWidthNotification.d.ts +6 -1
  105. package/dist/types/src/components/overlays/notification/MessageNotification.d.ts +15 -1
  106. package/dist/types/src/components/overlays/notification/MessageNotificationParts.d.ts +10 -5
  107. package/dist/types/src/components/overlays/notification/Notification.d.ts +23 -2
  108. package/dist/types/src/components/overlays/notification/NotificationActionButtons.d.ts +12 -0
  109. package/dist/types/src/components/overlays/notification/__tests__/supportTextLink.test.d.ts +1 -0
  110. package/dist/types/src/components/overlays/notification/linkTarget.d.ts +11 -0
  111. package/dist/types/src/utils/index.d.ts +1 -0
  112. package/dist/types/src/utils/url/__tests__/isSafeUrl.test.d.ts +1 -0
  113. package/dist/types/src/utils/url/index.d.ts +1 -0
  114. package/dist/types/src/utils/url/isSafeUrl.d.ts +8 -0
  115. package/dist/ui-admin/assets/styles/style.css +54 -34
  116. package/package.json +1 -1
@@ -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 };
@@ -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;