@iyulab/components 0.3.0 → 0.4.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 (75) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/assets/icons/arrow-down-up.svg.js +3 -0
  3. package/dist/assets/icons/code-slash.svg.js +3 -0
  4. package/dist/assets/icons/download.svg.js +3 -0
  5. package/dist/assets/icons/sort-alpha-down.svg.js +3 -0
  6. package/dist/assets/icons/sort-alpha-up.svg.js +3 -0
  7. package/dist/components/UElement.d.ts +59 -0
  8. package/dist/components/UElement.js +96 -0
  9. package/dist/components/{FloatingElement.d.ts → UFloatingElement.d.ts} +4 -4
  10. package/dist/components/{FloatingElement.js → UFloatingElement.js} +13 -13
  11. package/dist/components/UJsonElement.d.ts +46 -0
  12. package/dist/components/UJsonElement.js +93 -0
  13. package/dist/components/UJsonElement.styles.js +95 -0
  14. package/dist/components/{ModalElement.d.ts → UModalElement.d.ts} +4 -4
  15. package/dist/components/{ModalElement.js → UModalElement.js} +8 -8
  16. package/dist/components/UModalElement.styles.d.ts +1 -0
  17. package/dist/components/alert/UAlert.component.d.ts +3 -3
  18. package/dist/components/alert/UAlert.component.js +2 -2
  19. package/dist/components/button/UButton.component.d.ts +3 -3
  20. package/dist/components/button/UButton.component.js +2 -2
  21. package/dist/components/card/UCard.component.d.ts +2 -2
  22. package/dist/components/card/UCard.component.js +2 -2
  23. package/dist/components/carousel/UCarousel.component.d.ts +34 -23
  24. package/dist/components/carousel/UCarousel.component.js +124 -103
  25. package/dist/components/carousel/UCarousel.styles.js +22 -23
  26. package/dist/components/dialog/UDialog.component.d.ts +4 -4
  27. package/dist/components/dialog/UDialog.component.js +2 -2
  28. package/dist/components/divider/UDivider.component.d.ts +3 -3
  29. package/dist/components/divider/UDivider.component.js +2 -2
  30. package/dist/components/drawer/UDrawer.component.d.ts +4 -4
  31. package/dist/components/drawer/UDrawer.component.js +2 -2
  32. package/dist/components/form/UForm.component.d.ts +3 -3
  33. package/dist/components/form/UForm.component.js +2 -2
  34. package/dist/components/icon/UIcon.component.d.ts +3 -3
  35. package/dist/components/icon/UIcon.component.js +2 -2
  36. package/dist/components/input/UInput.component.d.ts +3 -3
  37. package/dist/components/input/UInput.component.js +2 -2
  38. package/dist/components/menu/UMenu.component.d.ts +4 -4
  39. package/dist/components/menu/UMenu.component.js +2 -2
  40. package/dist/components/menu-item/UMenuItem.component.d.ts +3 -3
  41. package/dist/components/menu-item/UMenuItem.component.js +2 -2
  42. package/dist/components/progress-bar/UProgressBar.component.d.ts +3 -3
  43. package/dist/components/progress-bar/UProgressBar.component.js +2 -2
  44. package/dist/components/progress-ring/UProgressRing.component.d.ts +3 -3
  45. package/dist/components/progress-ring/UProgressRing.component.js +2 -2
  46. package/dist/components/skeleton/USkeleton.component.d.ts +3 -3
  47. package/dist/components/skeleton/USkeleton.component.js +2 -2
  48. package/dist/components/spinner/USpinner.component.d.ts +3 -3
  49. package/dist/components/spinner/USpinner.component.js +2 -2
  50. package/dist/components/split-panel/USplitPanel.component.d.ts +3 -3
  51. package/dist/components/split-panel/USplitPanel.component.js +2 -2
  52. package/dist/components/tag/UTag.component.d.ts +3 -3
  53. package/dist/components/tag/UTag.component.js +2 -2
  54. package/dist/components/tooltip/UTooltip.component.d.ts +4 -4
  55. package/dist/components/tooltip/UTooltip.component.js +2 -2
  56. package/dist/components/tree/UTree.component.d.ts +3 -3
  57. package/dist/components/tree/UTree.component.js +2 -2
  58. package/dist/components/tree-item/UTreeItem.component.d.ts +3 -3
  59. package/dist/components/tree-item/UTreeItem.component.js +2 -2
  60. package/dist/index.d.ts +6 -0
  61. package/dist/index.js +6 -0
  62. package/dist/utilities/IconRegistry.js +16 -1
  63. package/dist/utilities/elements.d.ts +23 -0
  64. package/dist/utilities/elements.js +23 -1
  65. package/dist/utilities/sanitizers.d.ts +35 -0
  66. package/dist/utilities/sanitizers.js +43 -0
  67. package/package.json +4 -5
  68. package/dist/components/BaseElement.d.ts +0 -33
  69. package/dist/components/BaseElement.js +0 -59
  70. /package/dist/components/{BaseElement.styles.d.ts → UElement.styles.d.ts} +0 -0
  71. /package/dist/components/{BaseElement.styles.js → UElement.styles.js} +0 -0
  72. /package/dist/components/{FloatingElement.styles.d.ts → UFloatingElement.styles.d.ts} +0 -0
  73. /package/dist/components/{FloatingElement.styles.js → UFloatingElement.styles.js} +0 -0
  74. /package/dist/components/{ModalElement.styles.d.ts → UJsonElement.styles.d.ts} +0 -0
  75. /package/dist/components/{ModalElement.styles.js → UModalElement.styles.js} +0 -0
@@ -15,3 +15,26 @@ export declare function querySelectorWithin(element: Element, selectors: string)
15
15
  * - 탐색은 element가 존재하는 shadow DOM 또는 document 루트 범위에서 이루어집니다.
16
16
  */
17
17
  export declare function querySelectorAllWithin(element: Element, selectors: string): HTMLElement[];
18
+ /**
19
+ * 지정한 태그, 속성, 내용으로 커스텀 엘리먼트 HTML 문자열을 생성합니다.
20
+ *
21
+ * 속성 값 처리 규칙:
22
+ * - `null` / `undefined` → 해당 속성 생략
23
+ * - `false` (boolean) → 해당 속성 생략
24
+ * - `true` (boolean) → 값 없이 키만 출력 (예: `disabled`)
25
+ * - `object` → `JSON.stringify` 후 `escapeHtmlAttr`
26
+ * - `href` / `src` 키 → `escapeHtmlHref` 적용
27
+ * - 그 외 string → `escapeHtmlAttr` 적용
28
+ *
29
+ * `content`는 이미 안전한 HTML 문자열이라고 가정합니다 (내부 escape 없음).
30
+ *
31
+ * @param tag - 커스텀 엘리먼트 태그 이름 (예: `"u-ref-tag"`)
32
+ * @param attrs - 속성 객체
33
+ * @param content - 내부 HTML 문자열 (기본값: `""`)
34
+ * @returns 생성된 HTML 문자열
35
+ *
36
+ * @example
37
+ * buildElementHTML('u-ref-tag', { href: 'https://example.com', disabled: true }, 'Click');
38
+ * // → '<u-ref-tag href="https://example.com" disabled>Click</u-ref-tag>'
39
+ */
40
+ export declare function buildElementHTML(tag: string, attrs?: Record<string, string | boolean | object | null | undefined>, content?: string): string;
@@ -1,3 +1,5 @@
1
+ import { escapeHtmlHref, escapeHtmlAttr } from './sanitizers.js';
2
+
1
3
  function getParentElement(element) {
2
4
  if (element.parentElement) {
3
5
  return element.parentElement;
@@ -6,6 +8,15 @@ function getParentElement(element) {
6
8
  return root instanceof Document ? root.documentElement : root instanceof ShadowRoot ? root.host : root instanceof HTMLElement ? root : void 0;
7
9
  }
8
10
  }
11
+ function querySelectorWithin(element, selectors) {
12
+ if (!selectors) return null;
13
+ const rootNode = element.getRootNode({ composed: false });
14
+ if (rootNode instanceof ShadowRoot || rootNode instanceof Document) {
15
+ return rootNode.querySelector(selectors);
16
+ } else {
17
+ return null;
18
+ }
19
+ }
9
20
  function querySelectorAllWithin(element, selectors) {
10
21
  if (!selectors) return [];
11
22
  const rootNode = element.getRootNode({ composed: false });
@@ -16,5 +27,16 @@ function querySelectorAllWithin(element, selectors) {
16
27
  return [];
17
28
  }
18
29
  }
30
+ function buildElementHTML(tag, attrs = {}, content = "") {
31
+ const URL_ATTR = /* @__PURE__ */ new Set(["href", "src"]);
32
+ const attrStr = Object.entries(attrs).flatMap(([k, v]) => {
33
+ if (v === void 0 || v == null || v === false) return [];
34
+ if (v === true) return [k];
35
+ const raw = typeof v === "object" ? JSON.stringify(v) : String(v);
36
+ const escaped = URL_ATTR.has(k) ? escapeHtmlHref(raw) : escapeHtmlAttr(raw);
37
+ return [`${k}="${escaped}"`];
38
+ }).join(" ");
39
+ return attrStr ? `<${tag} ${attrStr}>${content}</${tag}>` : `<${tag}>${content}</${tag}>`;
40
+ }
19
41
 
20
- export { getParentElement, querySelectorAllWithin };
42
+ export { buildElementHTML, getParentElement, querySelectorAllWithin, querySelectorWithin };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * HTML 출력 시 XSS 방지를 위한 escape 유틸
3
+ *
4
+ * 컨텍스트에 맞는 함수를 사용해야 합니다.
5
+ *
6
+ * - escapeHtmlText : HTML 텍스트 노드에 삽입할 때
7
+ * - escapeHtmlAttr : HTML attribute 값에 삽입할 때
8
+ * - escapeHtmlHref : href/src 같은 URL attribute에 삽입할 때
9
+ */
10
+ /**
11
+ * Zero Width Space / LTR / RTL mark / BOM 등
12
+ * 화면에 표시되지 않는 유니코드 문자를 제거합니다.
13
+ */
14
+ export declare function stripZeroWidth(value: string): string;
15
+ /**
16
+ * HTML 텍스트 노드(context)에 삽입할 문자열을 escape합니다.
17
+ *
18
+ * 예:
19
+ * <script> → &lt;script&gt;
20
+ */
21
+ export declare function escapeHtmlText(value: string): string;
22
+ /**
23
+ * HTML attribute 값(context)에 삽입할 문자열을 escape합니다.
24
+ *
25
+ * 예:
26
+ * <div title="{value}">
27
+ */
28
+ export declare function escapeHtmlAttr(value: string): string;
29
+ /**
30
+ * href / src attribute용 문자열 처리
31
+ *
32
+ * 예:
33
+ * <a href="{value}">
34
+ */
35
+ export declare function escapeHtmlHref(value: string): string;
@@ -0,0 +1,43 @@
1
+ function stripZeroWidth(value) {
2
+ const ZERO_WIDTH_REGEX = /[\u200B\u200C\u200D\u200E\u200F\uFEFF]/g;
3
+ return ZERO_WIDTH_REGEX.test(value) ? value.replace(ZERO_WIDTH_REGEX, "") : value;
4
+ }
5
+ const HTML_TEXT_ESCAPE_REGEX = /[&<>"']/g;
6
+ const HTML_TEXT_ESCAPE_MAP = {
7
+ "&": "&amp;",
8
+ "<": "&lt;",
9
+ ">": "&gt;",
10
+ '"': "&quot;",
11
+ "'": "&#39;"
12
+ };
13
+ function escapeHtmlText(value) {
14
+ return HTML_TEXT_ESCAPE_REGEX.test(value) ? value.replace(HTML_TEXT_ESCAPE_REGEX, (ch) => HTML_TEXT_ESCAPE_MAP[ch]) : value;
15
+ }
16
+ const HTML_ATTR_ESCAPE_REGEX = /[&<>"]/g;
17
+ const HTML_ATTR_ESCAPE_MAP = {
18
+ "&": "&amp;",
19
+ "<": "&lt;",
20
+ ">": "&gt;",
21
+ '"': "&quot;"
22
+ };
23
+ function escapeHtmlAttr(value) {
24
+ return HTML_ATTR_ESCAPE_REGEX.test(value) ? value.replace(HTML_ATTR_ESCAPE_REGEX, (ch) => HTML_ATTR_ESCAPE_MAP[ch]) : value;
25
+ }
26
+ const HREF_STRIP_CHARS_REGEX = /[\u0000-\u001F\u007F\s]+/g;
27
+ const HREF_UNSAFE_PROTOCOL_REGEX = /^(?:javascript|data|vbscript):/i;
28
+ function escapeHtmlHref(value) {
29
+ let normalized = stripZeroWidth(value).trim();
30
+ if (HREF_STRIP_CHARS_REGEX.test(normalized)) {
31
+ normalized = normalized.replace(HREF_STRIP_CHARS_REGEX, "");
32
+ }
33
+ normalized = normalized.replace(/\\/g, "/");
34
+ if (normalized.startsWith("//")) {
35
+ return "#";
36
+ }
37
+ if (HREF_UNSAFE_PROTOCOL_REGEX.test(normalized)) {
38
+ return "#";
39
+ }
40
+ return escapeHtmlAttr(normalized);
41
+ }
42
+
43
+ export { escapeHtmlAttr, escapeHtmlHref, escapeHtmlText, stripZeroWidth };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/components",
3
3
  "description": "web-components library based on lit-element made by iyulab",
4
- "version": "0.3.0",
4
+ "version": "0.4.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "components",
@@ -41,7 +41,6 @@
41
41
  "dependencies": {
42
42
  "@floating-ui/dom": "^1.7.5",
43
43
  "lit": "^3.3.2",
44
- "quill": "2.0.3",
45
44
  "reflect-metadata": "^0.2.2"
46
45
  },
47
46
  "peerDependencies": {
@@ -57,9 +56,9 @@
57
56
  }
58
57
  },
59
58
  "devDependencies": {
60
- "@eslint/js": "^10.0.1",
61
- "@types/node": "^25.3.0",
62
- "eslint": "^10.0.2",
59
+ "@eslint/js": "^9.39.3",
60
+ "@types/node": "^25.3.2",
61
+ "eslint": "^9.39.3",
63
62
  "eslint-plugin-lit": "^2.2.1",
64
63
  "globals": "^17.3.0",
65
64
  "typescript": "^5.9.3",
@@ -1,33 +0,0 @@
1
- import { CSSResultGroup, LitElement } from 'lit';
2
- /**
3
- * BaseElement는 LitElement를 확장한 기본 Web Component 클래스입니다.
4
- * 이 클래스는 자동으로 종속된 컴포넌트를 정의하며, 유용한 헬퍼 메서드들을 제공합니다.
5
- */
6
- export declare class BaseElement extends LitElement {
7
- /**
8
- * 기본 스타일을 정의합니다.
9
- */
10
- static styles: CSSResultGroup;
11
- /**
12
- * 현재 컴포넌트에 종속된 컴포넌트들을 정의하는 정적 속성입니다.
13
- * 이 속성은 컴포넌트가 정의될 때 자동으로 호출되어
14
- * 종속된 컴포넌트를 등록합니다.
15
- */
16
- static dependencies: Record<string, typeof BaseElement>;
17
- constructor();
18
- /**
19
- * 커스텀 엘리먼트를 정의합니다.
20
- * 이미 정의된 엘리먼트는 다시 정의하지 않습니다.
21
- * @param name - 커스텀 엘리먼트의 이름
22
- * @param options - 엘리먼트 정의 옵션
23
- */
24
- static define(name: string, options?: ElementDefinitionOptions): void;
25
- /**
26
- * 커스텀 이벤트를 디스패치합니다.
27
- * @param name - 이벤트 이름
28
- * @param value - 이벤트의 detail에 포함될 데이터
29
- * @param options - 추가 이벤트 옵션 (예: bubbles, composed 등)
30
- * @returns 이벤트가 성공적으로 디스패치되었는지 여부
31
- */
32
- protected emit(name: string, value?: any, options?: CustomEventInit): boolean;
33
- }
@@ -1,59 +0,0 @@
1
- import { LitElement } from 'lit';
2
- import { styles } from './BaseElement.styles.js';
3
-
4
- class BaseElement extends LitElement {
5
- static {
6
- /**
7
- * 기본 스타일을 정의합니다.
8
- */
9
- this.styles = styles;
10
- }
11
- static {
12
- /**
13
- * 현재 컴포넌트에 종속된 컴포넌트들을 정의하는 정적 속성입니다.
14
- * 이 속성은 컴포넌트가 정의될 때 자동으로 호출되어
15
- * 종속된 컴포넌트를 등록합니다.
16
- */
17
- this.dependencies = {};
18
- }
19
- constructor() {
20
- super();
21
- Object.entries(this.constructor.dependencies).forEach(([name, component]) => {
22
- component.define(name);
23
- });
24
- }
25
- /**
26
- * 커스텀 엘리먼트를 정의합니다.
27
- * 이미 정의된 엘리먼트는 다시 정의하지 않습니다.
28
- * @param name - 커스텀 엘리먼트의 이름
29
- * @param options - 엘리먼트 정의 옵션
30
- */
31
- static define(name, options = {}) {
32
- if (!customElements.get(name)) {
33
- try {
34
- customElements.define(name, this, options);
35
- } catch (error) {
36
- console.warn(`Failed to register component "${name}":`, error);
37
- }
38
- }
39
- }
40
- /**
41
- * 커스텀 이벤트를 디스패치합니다.
42
- * @param name - 이벤트 이름
43
- * @param value - 이벤트의 detail에 포함될 데이터
44
- * @param options - 추가 이벤트 옵션 (예: bubbles, composed 등)
45
- * @returns 이벤트가 성공적으로 디스패치되었는지 여부
46
- */
47
- emit(name, value, options) {
48
- const event = new CustomEvent(name, {
49
- bubbles: true,
50
- composed: true,
51
- cancelable: false,
52
- detail: value,
53
- ...options
54
- });
55
- return this.dispatchEvent(event);
56
- }
57
- }
58
-
59
- export { BaseElement };