@simplysm/core-browser 13.0.69 → 13.0.70

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.
package/README.md CHANGED
@@ -1,239 +1,29 @@
1
1
  # @simplysm/core-browser
2
2
 
3
- A browser-specific utility package for the Simplysm framework. It provides frequently needed features in browser environments such as DOM element traversal, clipboard handling, Blob downloads, and binary downloads.
4
-
5
- This package includes both APIs that can be called directly as instance methods by extending `Element` and `HTMLElement` prototypes, and utilities in the form of static functions.
3
+ Simplysm package - Core module (browser)
6
4
 
7
5
  ## Installation
8
6
 
9
- ```bash
10
- npm install @simplysm/core-browser
11
- # or
12
7
  pnpm add @simplysm/core-browser
13
- ```
14
-
15
- ## Main Modules
16
-
17
- ### Element Extension Methods
18
-
19
- Instance methods automatically added to `Element.prototype` when importing `"@simplysm/core-browser"`. Works as a side-effect import.
20
-
21
- | Method | Return Type | Description |
22
- |--------|-----------|------|
23
- | `findAll<T>(selector: string)` | `T[]` | Search all descendant elements by selector. Returns empty array for empty selector. |
24
- | `findFirst<T>(selector: string)` | `T \| undefined` | Search first matching element by selector. Returns `undefined` for empty selector. |
25
- | `prependChild<T>(child: T)` | `T` | Insert element as first child |
26
- | `getParents()` | `Element[]` | Return list of all parent elements (closest first) |
27
- | `findFocusableParent()` | `HTMLElement \| undefined` | Search for first focusable element among parents (uses tabbable) |
28
- | `findFirstFocusableChild()` | `HTMLElement \| undefined` | Search for first focusable element among children (uses tabbable) |
29
- | `isOffsetElement()` | `boolean` | Check if element has `position: relative`, `absolute`, `fixed`, or `sticky` |
30
- | `isVisible()` | `boolean` | Check element visibility (clientRects exist, not `visibility: hidden`, not `opacity: 0`) |
31
-
32
- ### HTMLElement Extension Methods
33
-
34
- Instance methods automatically added to `HTMLElement.prototype`.
35
-
36
- | Method | Return Type | Description |
37
- |--------|-----------|------|
38
- | `repaint()` | `void` | Trigger forced repaint by causing forced synchronous layout |
39
- | `getRelativeOffset(parent: HTMLElement \| string)` | `{ top: number; left: number }` | Calculate document-based position relative to a parent element or CSS selector, suitable for CSS `top`/`left` (includes `window.scrollX/Y`, parent scroll, border widths, and CSS transforms) |
40
- | `scrollIntoViewIfNeeded(target: { top: number; left: number }, offset?: { top: number; left: number })` | `void` | Adjust scroll so that target position is visible past any fixed offset regions (top/left only) |
41
-
42
- ### Static Functions
43
-
44
- | Function | Return Type | Description |
45
- |------|-----------|------|
46
- | `copyElement(event: ClipboardEvent)` | `void` | Copy value of first `input`/`textarea` within the event target element to clipboard |
47
- | `pasteToElement(event: ClipboardEvent)` | `void` | Paste clipboard text into the first `input`/`textarea` within the event target element |
48
- | `getBounds(els: Element[], timeout?: number)` | `Promise<ElementBounds[]>` | Query element bounds using `IntersectionObserver`. Results are returned in input order. Default timeout: 5000ms. |
49
-
50
- ### Download Utilities
51
-
52
- | Function | Return Type | Description |
53
- |------|-----------|------|
54
- | `downloadBlob(blob: Blob, fileName: string)` | `void` | Download a Blob as a file |
55
- | `fetchUrlBytes(url: string, options?: { onProgress?: (progress: DownloadProgress) => void })` | `Promise<Uint8Array>` | Download binary data from a URL with optional progress callback |
56
-
57
- ### File Dialog
58
-
59
- | Function | Return Type | Description |
60
- |------|-----------|------|
61
- | `openFileDialog(options?: { accept?: string; multiple?: boolean })` | `Promise<File[] \| undefined>` | Open a file selection dialog. Returns `undefined` if the user cancels without selecting. |
62
-
63
- ### Types
64
-
65
- | Type | Description |
66
- |------|------|
67
- | `ElementBounds` | Element bounds information: `target: Element`, `top: number`, `left: number`, `width: number`, `height: number` |
68
- | `DownloadProgress` | Download progress: `receivedLength: number`, `contentLength: number` |
69
-
70
- ## Usage Examples
71
-
72
- ### Element Extension Methods
73
-
74
- ```typescript
75
- import "@simplysm/core-browser";
76
-
77
- // Search descendant elements
78
- const buttons = container.findAll<HTMLButtonElement>("button.primary");
79
- const firstInput = container.findFirst<HTMLInputElement>("input[type='text']");
80
-
81
- // Insert element as first child
82
- const newEl = document.createElement("div");
83
- container.prependChild(newEl);
84
-
85
- // Query all parent elements (closest parent first)
86
- const parents = element.getParents();
87
-
88
- // Search focusable elements (based on tabbable library)
89
- const focusableParent = element.findFocusableParent();
90
- const focusableChild = element.findFirstFocusableChild();
91
-
92
- // Check element state
93
- if (element.isOffsetElement()) {
94
- // position is one of relative, absolute, fixed, sticky
95
- }
96
-
97
- if (element.isVisible()) {
98
- // Element is visible on screen (clientRects exist, visibility !== "hidden", opacity !== "0")
99
- }
100
- ```
101
-
102
- ### HTMLElement Extension Methods
103
-
104
- ```typescript
105
- import "@simplysm/core-browser";
106
-
107
- // Forced repaint (useful for restarting CSS animations)
108
- element.repaint();
109
-
110
- // Calculate document-based position relative to a parent element (can be used directly for CSS top/left)
111
- const offset = element.getRelativeOffset(document.body);
112
- popup.style.top = `${offset.top}px`;
113
- popup.style.left = `${offset.left}px`;
114
-
115
- // Parent can also be specified by CSS selector (uses closest())
116
- const offset2 = element.getRelativeOffset(".scroll-container");
117
-
118
- // Adjust scroll position in table with fixed header/columns
119
- const scrollContainer = document.getElementById("table-body") as HTMLElement;
120
- const targetRow = document.getElementById("row-42") as HTMLElement;
121
-
122
- scrollContainer.scrollIntoViewIfNeeded(
123
- { top: targetRow.offsetTop, left: targetRow.offsetLeft },
124
- { top: 50, left: 120 }, // Fixed header height 50px, fixed column width 120px
125
- );
126
- ```
127
-
128
- ### Clipboard Handling
129
-
130
- ```typescript
131
- import { copyElement, pasteToElement } from "@simplysm/core-browser";
132
-
133
- // Use in copy event handler
134
- // Copies value of the first input/textarea within the target element to clipboard
135
- element.addEventListener("copy", (e) => copyElement(e));
136
-
137
- // Use in paste event handler
138
- // Pastes clipboard content into the first input/textarea within the target element
139
- element.addEventListener("paste", (e) => pasteToElement(e));
140
- ```
141
-
142
- ### Asynchronous Element Bounds Query
143
-
144
- ```typescript
145
- import { getBounds } from "@simplysm/core-browser";
146
- import type { ElementBounds } from "@simplysm/core-browser";
147
-
148
- const el1 = document.getElementById("item1")!;
149
- const el2 = document.getElementById("item2")!;
150
-
151
- // Query bounds information asynchronously using IntersectionObserver
152
- const bounds: ElementBounds[] = await getBounds([el1, el2]);
153
-
154
- for (const b of bounds) {
155
- console.log(b.target); // Target element being measured
156
- console.log(b.top); // Top position relative to viewport
157
- console.log(b.left); // Left position relative to viewport
158
- console.log(b.width); // Element width
159
- console.log(b.height); // Element height
160
- }
161
-
162
- // Specify custom timeout (default: 5000ms)
163
- // TimeoutError is thrown if timeout is exceeded
164
- const bounds2 = await getBounds([el1], 3000);
165
- ```
166
-
167
- ### Blob Download
168
-
169
- ```typescript
170
- import { downloadBlob } from "@simplysm/core-browser";
171
-
172
- // Download Blob object as file
173
- const blob = new Blob(["Hello, World!"], { type: "text/plain" });
174
- downloadBlob(blob, "hello.txt");
175
-
176
- // Download binary data such as Excel files
177
- const excelBlob = new Blob([excelData], {
178
- type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
179
- });
180
- downloadBlob(excelBlob, "report.xlsx");
181
- ```
182
-
183
- ### Binary Download (with Progress Support)
184
-
185
- ```typescript
186
- import { fetchUrlBytes } from "@simplysm/core-browser";
187
- import type { DownloadProgress } from "@simplysm/core-browser";
188
-
189
- // Basic usage
190
- const data: Uint8Array = await fetchUrlBytes("https://example.com/file.bin");
191
-
192
- // Using progress callback
193
- const data2 = await fetchUrlBytes("https://example.com/large-file.zip", {
194
- onProgress: (progress: DownloadProgress) => {
195
- const percent = progress.contentLength > 0
196
- ? Math.round((progress.receivedLength / progress.contentLength) * 100)
197
- : 0;
198
- console.log(`Download progress: ${percent}%`);
199
- },
200
- });
201
- ```
202
8
 
203
- ### File Selection Dialog
9
+ **Peer Dependencies:** none
204
10
 
205
- ```typescript
206
- import { openFileDialog } from "@simplysm/core-browser";
11
+ ## Source Index
207
12
 
208
- // Open file selection dialog (single file)
209
- const files = await openFileDialog({
210
- accept: "image/*", // Optional: specify accepted file types
211
- });
212
- if (files) {
213
- console.log("Selected file:", files[0].name);
214
- }
13
+ ### Extensions
215
14
 
216
- // Open file selection dialog (multiple files)
217
- const selectedFiles = await openFileDialog({
218
- accept: ".pdf,.doc,.docx",
219
- multiple: true,
220
- });
221
- if (selectedFiles) {
222
- selectedFiles.forEach((file) => {
223
- console.log("File:", file.name, "Size:", file.size);
224
- });
225
- }
226
- ```
15
+ | Source | Exports | Description | Test |
16
+ |--------|---------|-------------|------|
17
+ | `src/extensions/element-ext.ts` | `ElementBounds`, `copyElement`, `pasteToElement`, `getBounds` | DOM clipboard operations and element bounds utilities | `element-ext.spec.ts` |
18
+ | `src/extensions/html-element-ext.ts` | _(HTMLElement prototype augmentation only)_ | Repaint, relative offset, and scroll-into-view helpers | `html-element-ext.spec.ts` |
227
19
 
228
- ## Caveats
20
+ ### Utils
229
21
 
230
- - This package is **browser-only**. It cannot be used in Node.js environments.
231
- - `Element` and `HTMLElement` prototype extensions work as **side-effect imports**. Extensions are automatically applied when importing `"@simplysm/core-browser"` or any item from the package.
232
- - The `getBounds()` function uses `IntersectionObserver` and works asynchronously. If all elements are not observed within the specified timeout (default 5000ms), a `TimeoutError` is thrown. Duplicate elements in the input array are deduplicated; results are returned in input order.
233
- - The `getRelativeOffset()` method accepts either an `HTMLElement` or a CSS selector string as the `parent` parameter. When a string is given, it uses `closest()` to find the nearest matching ancestor. It throws `ArgumentError` if the parent element cannot be found. The returned coordinates are document-based (include `window.scrollX/Y`) and are suitable for use as CSS `top`/`left` values. Intermediate element border widths, parent scroll position, and CSS `transform` are all included in the calculation.
234
- - The `scrollIntoViewIfNeeded()` method only handles cases where the target is beyond the top/left boundaries. The bottom/right directions rely on the browser's default focus scrolling.
235
- - The `fetchUrlBytes()` function improves memory efficiency by pre-allocating a buffer when the `Content-Length` header is available, and collects and merges chunks for chunked encoding. Throws an `Error` if the response status is not ok or if the response body is not readable.
236
- - The `pasteToElement()` function replaces the entire value of the target input/textarea. It does not consider cursor position or selection range. It dispatches an `input` event after the value is set.
22
+ | Source | Exports | Description | Test |
23
+ |--------|---------|-------------|------|
24
+ | `src/utils/download.ts` | `downloadBlob` | Trigger browser file download from a Blob | `download.spec.ts` |
25
+ | `src/utils/fetch.ts` | `DownloadProgress`, `fetchUrlBytes` | Fetch URL as Uint8Array with progress callback | - |
26
+ | `src/utils/file-dialog.ts` | `openFileDialog` | Open native file selection dialog programmatically | - |
237
27
 
238
28
  ## License
239
29
 
@@ -1,98 +1,98 @@
1
1
  /**
2
- * 요소 bounds 정보 타입
2
+ * Element bounds information type
3
3
  */
4
4
  export interface ElementBounds {
5
- /** 측정 대상 요소 */
5
+ /** Element to be measured */
6
6
  target: Element;
7
- /** 뷰포트 기준 상단 위치 */
7
+ /** Top position relative to viewport */
8
8
  top: number;
9
- /** 뷰포트 기준 왼쪽 위치 */
9
+ /** Left position relative to viewport */
10
10
  left: number;
11
- /** 요소 너비 */
11
+ /** Element width */
12
12
  width: number;
13
- /** 요소 높이 */
13
+ /** Element height */
14
14
  height: number;
15
15
  }
16
16
  declare global {
17
17
  interface Element {
18
18
  /**
19
- * 셀렉터로 하위 요소 전체 검색
19
+ * Find all child elements matching selector
20
20
  *
21
- * @param selector - CSS 셀렉터
22
- * @returns 매칭된 요소 배열 ( 셀렉터는 배열 반환)
21
+ * @param selector - CSS selector
22
+ * @returns Array of matching elements (empty selector returns empty array)
23
23
  */
24
24
  findAll<T extends Element = Element>(selector: string): T[];
25
25
  /**
26
- * 셀렉터로 번째 매칭 요소 검색
26
+ * Find first element matching selector
27
27
  *
28
- * @param selector - CSS 셀렉터
29
- * @returns 번째 매칭 요소 또는 undefined ( 셀렉터는 undefined 반환)
28
+ * @param selector - CSS selector
29
+ * @returns First matching element or undefined (empty selector returns undefined)
30
30
  */
31
31
  findFirst<T extends Element = Element>(selector: string): T | undefined;
32
32
  /**
33
- * 요소를 번째 자식으로 삽입
33
+ * Insert element as first child
34
34
  *
35
- * @param child - 삽입할 자식 요소
36
- * @returns 삽입된 자식 요소
35
+ * @param child - Child element to insert
36
+ * @returns Inserted child element
37
37
  */
38
38
  prependChild<T extends Element>(child: T): T;
39
39
  /**
40
- * 모든 부모 요소 목록 반환 (가까운 순서)
40
+ * Get all parent elements (in order of proximity)
41
41
  *
42
- * @returns 부모 요소 배열 (가까운 부모부터 순서대로)
42
+ * @returns Array of parent elements (from closest to farthest)
43
43
  */
44
44
  getParents(): Element[];
45
45
  /**
46
- * 부모 번째 포커스 가능 요소 검색 (tabbable 사용)
46
+ * Find first focusable parent element (using tabbable)
47
47
  *
48
- * @returns 포커스 가능한 번째 부모 요소 또는 undefined
48
+ * @returns First focusable parent element or undefined
49
49
  */
50
50
  findFocusableParent(): HTMLElement | undefined;
51
51
  /**
52
- * 자식 번째 포커스 가능 요소 검색 (tabbable 사용)
52
+ * Find first focusable child element (using tabbable)
53
53
  *
54
- * @returns 포커스 가능한 번째 자식 요소 또는 undefined
54
+ * @returns First focusable child element or undefined
55
55
  */
56
56
  findFirstFocusableChild(): HTMLElement | undefined;
57
57
  /**
58
- * 요소가 offset 기준 요소인지 확인 (position: relative/absolute/fixed/sticky)
58
+ * Check if element is an offset parent (position: relative/absolute/fixed/sticky)
59
59
  *
60
- * @returns position 속성이 relative, absolute, fixed, sticky 중 하나면 true
60
+ * @returns true if position property is one of relative, absolute, fixed, or sticky
61
61
  */
62
62
  isOffsetElement(): boolean;
63
63
  /**
64
- * 요소가 화면에 보이는지 확인
64
+ * Check if element is visible on screen
65
65
  *
66
66
  * @remarks
67
- * clientRects 존재 여부, visibility: hidden, opacity: 0 여부를 확인한다.
67
+ * Checks existence of clientRects, visibility: hidden, and opacity: 0.
68
68
  *
69
- * @returns 요소가 화면에 보이면 true
69
+ * @returns true if element is visible on screen
70
70
  */
71
71
  isVisible(): boolean;
72
72
  }
73
73
  }
74
74
  /**
75
- * 요소 내용을 클립보드에 복사 (copy 이벤트 핸들러에서 사용)
75
+ * Copy element content to clipboard (use with copy event handler)
76
76
  *
77
- * @param event - copy 이벤트 객체
77
+ * @param event - copy event object
78
78
  */
79
79
  export declare function copyElement(event: ClipboardEvent): void;
80
80
  /**
81
- * 클립보드 내용을 요소에 붙여넣기 (paste 이벤트 핸들러에서 사용)
81
+ * Paste clipboard content to element (use with paste event handler)
82
82
  *
83
83
  * @remarks
84
- * 대상 요소 내의 첫 번째 input/textarea 찾아 전체 값을 클립보드 내용으로 교체한다.
85
- * 커서 위치나 선택 영역을 고려하지 않는다.
84
+ * Finds the first input/textarea within the target element and replaces its entire value with clipboard content.
85
+ * Does not consider cursor position or selection.
86
86
  *
87
- * @param event - paste 이벤트 객체
87
+ * @param event - paste event object
88
88
  */
89
89
  export declare function pasteToElement(event: ClipboardEvent): void;
90
90
  /**
91
- * IntersectionObserver를 사용하여 요소들의 bounds 정보 조회
91
+ * Get bounds information for elements using IntersectionObserver
92
92
  *
93
- * @param els - 대상 요소 배열
94
- * @param timeout - 타임아웃 (밀리초, 기본: 5000)
95
- * @throws {TimeoutError} 타임아웃 시간 내에 응답이 없을 경우
93
+ * @param els - Array of target elements
94
+ * @param timeout - Timeout in milliseconds (default: 5000)
95
+ * @throws {TimeoutError} If no response within timeout duration
96
96
  */
97
97
  export declare function getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>;
98
98
  //# sourceMappingURL=element-ext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"element-ext.d.ts","sourceRoot":"","sources":["..\\..\\src\\extensions\\element-ext.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,mBAAmB;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,YAAY;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO;QACf;;;;;WAKG;QACH,OAAO,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC;QAE5D;;;;;WAKG;QACH,SAAS,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;QAExE;;;;;WAKG;QACH,YAAY,CAAC,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;QAE7C;;;;WAIG;QACH,UAAU,IAAI,OAAO,EAAE,CAAC;QAExB;;;;WAIG;QACH,mBAAmB,IAAI,WAAW,GAAG,SAAS,CAAC;QAE/C;;;;WAIG;QACH,uBAAuB,IAAI,WAAW,GAAG,SAAS,CAAC;QAEnD;;;;WAIG;QACH,eAAe,IAAI,OAAO,CAAC;QAE3B;;;;;;;WAOG;QACH,SAAS,IAAI,OAAO,CAAC;KACtB;CACF;AAkED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAYvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAa1D;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,OAAO,GAAE,MAAa,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAoDhG"}
1
+ {"version":3,"file":"element-ext.d.ts","sourceRoot":"","sources":["..\\..\\src\\extensions\\element-ext.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO;QACf;;;;;WAKG;QACH,OAAO,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC;QAE5D;;;;;WAKG;QACH,SAAS,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;QAExE;;;;;WAKG;QACH,YAAY,CAAC,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;QAE7C;;;;WAIG;QACH,UAAU,IAAI,OAAO,EAAE,CAAC;QAExB;;;;WAIG;QACH,mBAAmB,IAAI,WAAW,GAAG,SAAS,CAAC;QAE/C;;;;WAIG;QACH,uBAAuB,IAAI,WAAW,GAAG,SAAS,CAAC;QAEnD;;;;WAIG;QACH,eAAe,IAAI,OAAO,CAAC;QAE3B;;;;;;;WAOG;QACH,SAAS,IAAI,OAAO,CAAC;KACtB;CACF;AAkED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAYvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAa1D;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,OAAO,GAAE,MAAa,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAoDhG"}
@@ -111,7 +111,7 @@ async function getBounds(els, timeout = 5e3) {
111
111
  }
112
112
  }),
113
113
  new Promise(
114
- (_, reject) => setTimeout(() => reject(new TimeoutError(void 0, `${timeout}ms \uCD08\uACFC`)), timeout)
114
+ (_, reject) => setTimeout(() => reject(new TimeoutError(void 0, `${timeout}ms timeout`)), timeout)
115
115
  )
116
116
  ]);
117
117
  } finally {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/extensions/element-ext.ts"],
4
- "mappings": "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",
4
+ "mappings": "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",
5
5
  "names": []
6
6
  }
@@ -1,45 +1,45 @@
1
1
  declare global {
2
2
  interface HTMLElement {
3
3
  /**
4
- * 강제 리페인트 (reflow 트리거)
4
+ * Force repaint (triggers reflow)
5
5
  */
6
6
  repaint(): void;
7
7
  /**
8
- * 부모 요소 기준 상대 위치 계산 (CSS 포지셔닝용)
8
+ * Calculate relative position based on parent element (for CSS positioning)
9
9
  *
10
10
  * @remarks
11
- * 함수는 요소의 위치를 부모 요소 기준으로 계산하되, `window.scrollX/Y`를 포함하여
12
- * CSS `top`/`left` 속성에 직접 사용할 있는 문서 기준 좌표를 반환한다.
11
+ * Calculates element position relative to parent element, returning document-based coordinates
12
+ * including `window.scrollX/Y` that can be directly used in CSS `top`/`left` properties.
13
13
  *
14
- * 주요 사용 사례:
15
- * - 드롭다운, 팝업 등을 `document.body`에 append 후 위치 지정
16
- * - 스크롤된 페이지에서도 올바르게 동작
14
+ * Common use cases:
15
+ * - Position dropdowns, popups after appending to `document.body`
16
+ * - Works correctly on scrolled pages
17
17
  *
18
- * 계산에 포함되는 요소:
19
- * - 뷰포트 기준 위치 (getBoundingClientRect)
20
- * - 문서 스크롤 위치 (window.scrollX/Y)
21
- * - 부모 요소 내부 스크롤 (parentEl.scrollTop/Left)
22
- * - 중간 요소들의 border 두께
23
- * - CSS transform 변환
18
+ * Factors included in calculation:
19
+ * - Viewport-relative position (getBoundingClientRect)
20
+ * - Document scroll position (window.scrollX/Y)
21
+ * - Parent element internal scroll (parentEl.scrollTop/Left)
22
+ * - Border thickness of intermediate elements
23
+ * - CSS transform transformations
24
24
  *
25
- * @param parent - 기준이 부모 요소 또는 셀렉터 (예: document.body, ".container")
26
- * @returns CSS top/left 속성에 사용할 수 있는 좌표
27
- * @throws {ArgumentError} 부모 요소를 찾을 없는 경우
25
+ * @param parent - Parent element or selector to use as reference (e.g., document.body, ".container")
26
+ * @returns Coordinates usable in CSS top/left properties
27
+ * @throws {ArgumentError} If parent element cannot be found
28
28
  */
29
29
  getRelativeOffset(parent: HTMLElement | string): {
30
30
  top: number;
31
31
  left: number;
32
32
  };
33
33
  /**
34
- * 대상이 offset 영역(고정 헤더/고정 등)에 가려진 경우, 보이도록 스크롤
34
+ * Scroll to make target visible if hidden by offset area (e.g., fixed header/column)
35
35
  *
36
36
  * @remarks
37
- * 함수는 대상이 스크롤 영역의 위쪽/왼쪽 경계를 벗어난 경우만 처리한다.
38
- * 아래쪽/오른쪽으로 스크롤이 필요한 경우는 브라우저의 기본 포커스 스크롤 동작에 의존한다.
39
- * 주로 고정 헤더나 고정 열이 있는 테이블에서 포커스 이벤트와 함께 사용된다.
37
+ * Only handles cases where target extends beyond top/left boundaries of scroll area.
38
+ * For scrolling needed downward/rightward, relies on browser's default focus scroll behavior.
39
+ * Typically used with focus events on tables with fixed headers or columns.
40
40
  *
41
- * @param target - 대상의 컨테이너 위치 (offsetTop, offsetLeft)
42
- * @param offset - 가려지면 되는 영역 크기 (예: 고정 헤더 높이, 고정 너비)
41
+ * @param target - Target position within container (offsetTop, offsetLeft)
42
+ * @param offset - Size of area that must not be obscured (e.g., fixed header height, fixed column width)
43
43
  */
44
44
  scrollIntoViewIfNeeded(target: {
45
45
  top: number;
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Blob 파일로 다운로드
2
+ * Download Blob as file
3
3
  *
4
- * @param blob - 다운로드할 Blob 객체
5
- * @param fileName - 저장될 파일 이름
4
+ * @param blob - Blob object to download
5
+ * @param fileName - File name to save as
6
6
  */
7
7
  export declare function downloadBlob(blob: Blob, fileName: string): void;
8
8
  //# sourceMappingURL=download.d.ts.map
@@ -3,7 +3,7 @@ export interface DownloadProgress {
3
3
  contentLength: number;
4
4
  }
5
5
  /**
6
- * URL에서 바이너리 데이터 다운로드 (진행률 콜백 지원)
6
+ * Download binary data from URL (with progress callback support)
7
7
  */
8
8
  export declare function fetchUrlBytes(url: string, options?: {
9
9
  onProgress?: (progress: DownloadProgress) => void;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 파일 선택 다이얼로그를 프로그래밍 방식으로 열기
2
+ * Programmatically open file selection dialog
3
3
  */
4
4
  export declare function openFileDialog(options?: {
5
5
  accept?: string;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@simplysm/core-browser",
3
- "version": "13.0.69",
4
- "description": "심플리즘 패키지 - 코어 모듈 (browser)",
5
- "author": "김석래",
3
+ "version": "13.0.70",
4
+ "description": "Simplysm package - Core module (browser)",
5
+ "author": "simplysm",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
@@ -14,7 +14,8 @@
14
14
  "types": "./dist/index.d.ts",
15
15
  "files": [
16
16
  "dist",
17
- "src"
17
+ "src",
18
+ "tests"
18
19
  ],
19
20
  "sideEffects": [
20
21
  "./dist/extensions/element-ext.js",
@@ -22,7 +23,7 @@
22
23
  ],
23
24
  "dependencies": {
24
25
  "tabbable": "^6.4.0",
25
- "@simplysm/core-common": "13.0.69"
26
+ "@simplysm/core-common": "13.0.70"
26
27
  },
27
28
  "devDependencies": {
28
29
  "happy-dom": "^20.7.0"