@simplysm/core-browser 14.0.4 → 14.0.6
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 +95 -168
- package/docs/download.md +16 -0
- package/docs/element-extensions.md +158 -0
- package/docs/fetch.md +39 -0
- package/docs/file-dialog.md +21 -0
- package/docs/html-element-extensions.md +50 -0
- package/docs/indexed-db-store.md +103 -0
- package/docs/indexed-db-virtual-fs.md +107 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @simplysm/core-browser
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Browser-specific core utilities for the Simplysm framework. Provides DOM element extensions, download/fetch helpers, file dialog, and IndexedDB abstractions.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,217 +8,144 @@ Core browser utilities -- DOM extensions, file operations, IndexedDB wrappers.
|
|
|
8
8
|
npm install @simplysm/core-browser
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## API
|
|
11
|
+
## API Overview
|
|
12
12
|
|
|
13
|
-
### Extensions
|
|
13
|
+
### Element Extensions
|
|
14
14
|
|
|
15
|
-
|
|
|
16
|
-
|
|
17
|
-
| `ElementBounds` | interface |
|
|
18
|
-
| Element
|
|
19
|
-
|
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
15
|
+
| API | Type | Description |
|
|
16
|
+
|-----|------|-------------|
|
|
17
|
+
| `ElementBounds` | interface | Bounding rect info for an element |
|
|
18
|
+
| `Element.prototype.findAll` | method | Find all descendant elements matching a CSS selector |
|
|
19
|
+
| `Element.prototype.findFirst` | method | Find the first descendant element matching a CSS selector |
|
|
20
|
+
| `Element.prototype.prependChild` | method | Insert a child as the first element child |
|
|
21
|
+
| `Element.prototype.getParents` | method | Get all ancestor elements (nearest first) |
|
|
22
|
+
| `Element.prototype.findFocusableParent` | method | Find the nearest focusable ancestor |
|
|
23
|
+
| `Element.prototype.findFirstFocusableChild` | method | Find the first focusable descendant |
|
|
24
|
+
| `Element.prototype.isOffsetElement` | method | Check if element is an offset parent |
|
|
25
|
+
| `Element.prototype.isVisible` | method | Check if element is visible on screen |
|
|
26
|
+
| `copyElement` | function | Copy element content to clipboard via ClipboardEvent |
|
|
27
|
+
| `pasteToElement` | function | Paste clipboard content into element via ClipboardEvent |
|
|
28
|
+
| `getBounds` | function | Get bounding info for multiple elements using IntersectionObserver |
|
|
23
29
|
|
|
24
|
-
|
|
30
|
+
> See [docs/element-extensions.md](./docs/element-extensions.md) for details.
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|--------|------|-------------|
|
|
28
|
-
| `downloadBlob` | function | Download a Blob as a file |
|
|
29
|
-
| `DownloadProgress` | interface | Progress info for byte fetching |
|
|
30
|
-
| `fetchUrlBytes` | async function | Fetch URL content as `Uint8Array` with progress |
|
|
31
|
-
| `openFileDialog` | function | Open a native file picker dialog |
|
|
32
|
-
| `StoreConfig` | interface | IndexedDB store configuration |
|
|
33
|
-
| `IndexedDbStore` | class | IndexedDB key-value store wrapper |
|
|
34
|
-
| `VirtualFsEntry` | interface | Virtual filesystem entry descriptor |
|
|
35
|
-
| `IndexedDbVirtualFs` | class | Virtual filesystem backed by IndexedDB |
|
|
36
|
-
|
|
37
|
-
## Extensions
|
|
38
|
-
|
|
39
|
-
### ElementBounds (interface)
|
|
40
|
-
|
|
41
|
-
```ts
|
|
42
|
-
interface ElementBounds {
|
|
43
|
-
target: Element;
|
|
44
|
-
top: number;
|
|
45
|
-
left: number;
|
|
46
|
-
width: number;
|
|
47
|
-
height: number;
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Element prototype extensions (side-effect import)
|
|
52
|
-
|
|
53
|
-
Import `@simplysm/core-browser` to activate these extensions on `Element.prototype`.
|
|
32
|
+
### HTML Element Extensions
|
|
54
33
|
|
|
55
|
-
|
|
|
56
|
-
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `getParents` | `getParents(): Element[]` | Get all ancestor elements |
|
|
61
|
-
| `findFocusableParent` | `findFocusableParent(): HTMLElement \| undefined` | Find nearest focusable ancestor |
|
|
62
|
-
| `findFirstFocusableChild` | `findFirstFocusableChild(): HTMLElement \| undefined` | Find first focusable descendant |
|
|
63
|
-
| `isOffsetElement` | `isOffsetElement(): boolean` | Check if element is an offset parent |
|
|
64
|
-
| `isVisible` | `isVisible(): boolean` | Check if element is visible |
|
|
34
|
+
| API | Type | Description |
|
|
35
|
+
|-----|------|-------------|
|
|
36
|
+
| `HTMLElement.prototype.repaint` | method | Force a synchronous repaint |
|
|
37
|
+
| `HTMLElement.prototype.getRelativeOffset` | method | Calculate position relative to a parent element |
|
|
38
|
+
| `HTMLElement.prototype.scrollIntoViewIfNeeded` | method | Scroll container so target is not hidden by offset areas |
|
|
65
39
|
|
|
66
|
-
|
|
40
|
+
> See [docs/html-element-extensions.md](./docs/html-element-extensions.md) for details.
|
|
67
41
|
|
|
68
|
-
|
|
69
|
-
|--------|-----------|-------------|
|
|
70
|
-
| `repaint` | `repaint(): void` | Force a repaint of the element |
|
|
71
|
-
| `getRelativeOffset` | `getRelativeOffset(parent: HTMLElement \| string): { top: number; left: number }` | Get position relative to a parent |
|
|
72
|
-
| `scrollIntoViewIfNeeded` | `scrollIntoViewIfNeeded(target: { top: number; left: number }, offset?: { top: number; left: number }): void` | Scroll to make target position visible |
|
|
42
|
+
### Download
|
|
73
43
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
function copyElement(event: ClipboardEvent): void
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Copy the content of the focused element to the clipboard via a `ClipboardEvent`.
|
|
44
|
+
| API | Type | Description |
|
|
45
|
+
|-----|------|-------------|
|
|
46
|
+
| `downloadBlob` | function | Download a Blob as a file |
|
|
81
47
|
|
|
82
|
-
|
|
48
|
+
> See [docs/download.md](./docs/download.md) for details.
|
|
83
49
|
|
|
84
|
-
|
|
85
|
-
function pasteToElement(event: ClipboardEvent): void
|
|
86
|
-
```
|
|
50
|
+
### Fetch
|
|
87
51
|
|
|
88
|
-
|
|
52
|
+
| API | Type | Description |
|
|
53
|
+
|-----|------|-------------|
|
|
54
|
+
| `DownloadProgress` | interface | Progress info for fetch downloads |
|
|
55
|
+
| `fetchUrlBytes` | function | Download binary data from a URL with progress callback |
|
|
89
56
|
|
|
90
|
-
|
|
57
|
+
> See [docs/fetch.md](./docs/fetch.md) for details.
|
|
91
58
|
|
|
92
|
-
|
|
93
|
-
async function getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>
|
|
94
|
-
```
|
|
59
|
+
### File Dialog
|
|
95
60
|
|
|
96
|
-
|
|
61
|
+
| API | Type | Description |
|
|
62
|
+
|-----|------|-------------|
|
|
63
|
+
| `openFileDialog` | function | Programmatically open a file picker dialog |
|
|
97
64
|
|
|
98
|
-
|
|
65
|
+
> See [docs/file-dialog.md](./docs/file-dialog.md) for details.
|
|
99
66
|
|
|
100
|
-
###
|
|
67
|
+
### IndexedDB Store
|
|
101
68
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
69
|
+
| API | Type | Description |
|
|
70
|
+
|-----|------|-------------|
|
|
71
|
+
| `StoreConfig` | interface | Configuration for an IndexedDB object store |
|
|
72
|
+
| `IndexedDbStore` | class | Generic IndexedDB wrapper with CRUD operations |
|
|
105
73
|
|
|
106
|
-
|
|
74
|
+
> See [docs/indexed-db-store.md](./docs/indexed-db-store.md) for details.
|
|
107
75
|
|
|
108
|
-
###
|
|
76
|
+
### IndexedDB Virtual FS
|
|
109
77
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
```
|
|
78
|
+
| API | Type | Description |
|
|
79
|
+
|-----|------|-------------|
|
|
80
|
+
| `VirtualFsEntry` | interface | Entry representing a file or directory in the virtual FS |
|
|
81
|
+
| `IndexedDbVirtualFs` | class | Virtual file system backed by IndexedDB |
|
|
116
82
|
|
|
117
|
-
|
|
83
|
+
> See [docs/indexed-db-virtual-fs.md](./docs/indexed-db-virtual-fs.md) for details.
|
|
118
84
|
|
|
119
|
-
|
|
120
|
-
async function fetchUrlBytes(
|
|
121
|
-
url: string,
|
|
122
|
-
options?: { onProgress?: (progress: DownloadProgress) => void },
|
|
123
|
-
): Promise<Uint8Array>
|
|
124
|
-
```
|
|
85
|
+
## Usage Examples
|
|
125
86
|
|
|
126
|
-
|
|
87
|
+
### Copy/Paste with ClipboardEvent
|
|
127
88
|
|
|
128
|
-
|
|
89
|
+
```typescript
|
|
90
|
+
import { copyElement, pasteToElement } from "@simplysm/core-browser";
|
|
129
91
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
accept?: string;
|
|
133
|
-
multiple?: boolean;
|
|
134
|
-
}): Promise<File[] | undefined>
|
|
92
|
+
document.addEventListener("copy", (event) => copyElement(event));
|
|
93
|
+
document.addEventListener("paste", (event) => pasteToElement(event));
|
|
135
94
|
```
|
|
136
95
|
|
|
137
|
-
|
|
96
|
+
### Download a Blob
|
|
138
97
|
|
|
139
|
-
|
|
98
|
+
```typescript
|
|
99
|
+
import { downloadBlob } from "@simplysm/core-browser";
|
|
140
100
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
name: string;
|
|
144
|
-
keyPath: string;
|
|
145
|
-
}
|
|
101
|
+
const blob = new Blob(["hello"], { type: "text/plain" });
|
|
102
|
+
downloadBlob(blob, "hello.txt");
|
|
146
103
|
```
|
|
147
104
|
|
|
148
|
-
###
|
|
105
|
+
### Fetch binary data with progress
|
|
149
106
|
|
|
150
|
-
|
|
107
|
+
```typescript
|
|
108
|
+
import { fetchUrlBytes } from "@simplysm/core-browser";
|
|
151
109
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
110
|
+
const data = await fetchUrlBytes("/api/file.bin", {
|
|
111
|
+
onProgress: ({ receivedLength, contentLength }) => {
|
|
112
|
+
console.log(`${receivedLength} / ${contentLength}`);
|
|
113
|
+
},
|
|
114
|
+
});
|
|
156
115
|
```
|
|
157
116
|
|
|
158
|
-
|
|
117
|
+
### Open a file dialog
|
|
159
118
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
| `open` | `open(): Promise<IDBDatabase>` | Open the database connection |
|
|
163
|
-
| `withStore` | `withStore<T>(storeName: string, mode: IDBTransactionMode, fn: (store: IDBObjectStore) => IDBRequest<T>): Promise<T>` | Execute a single-store transaction |
|
|
164
|
-
| `get` | `get<T>(storeName: string, key: IDBValidKey): Promise<T \| undefined>` | Get a value by key |
|
|
165
|
-
| `put` | `put(storeName: string, value: unknown): Promise<void>` | Insert or update a value |
|
|
166
|
-
| `delete` | `delete(storeName: string, key: IDBValidKey): Promise<void>` | Delete a value by key |
|
|
167
|
-
| `getAll` | `getAll<T>(storeName: string): Promise<T[]>` | Get all values in a store |
|
|
168
|
-
| `close` | `close(): void` | Close the database connection |
|
|
119
|
+
```typescript
|
|
120
|
+
import { openFileDialog } from "@simplysm/core-browser";
|
|
169
121
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
dataBase64?: string;
|
|
122
|
+
const files = await openFileDialog({ accept: ".csv", multiple: true });
|
|
123
|
+
if (files) {
|
|
124
|
+
for (const file of files) {
|
|
125
|
+
console.log(file.name);
|
|
126
|
+
}
|
|
176
127
|
}
|
|
177
128
|
```
|
|
178
129
|
|
|
179
|
-
###
|
|
180
|
-
|
|
181
|
-
Virtual filesystem backed by an `IndexedDbStore`. Stores files and directories as entries in IndexedDB.
|
|
182
|
-
|
|
183
|
-
**Constructor:**
|
|
184
|
-
|
|
185
|
-
```ts
|
|
186
|
-
constructor(db: IndexedDbStore, storeName: string, keyField: string)
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
**Methods:**
|
|
190
|
-
|
|
191
|
-
| Method | Signature | Description |
|
|
192
|
-
|--------|-----------|-------------|
|
|
193
|
-
| `getEntry` | `getEntry(fullKey: string): Promise<VirtualFsEntry \| undefined>` | Get a filesystem entry by key |
|
|
194
|
-
| `putEntry` | `putEntry(fullKey: string, kind: "file" \| "dir", dataBase64?: string): Promise<void>` | Create or update a filesystem entry |
|
|
195
|
-
| `deleteByPrefix` | `deleteByPrefix(keyPrefix: string): Promise<boolean>` | Delete all entries matching a key prefix |
|
|
196
|
-
| `listChildren` | `listChildren(prefix: string): Promise<{ name: string; isDirectory: boolean }[]>` | List immediate children of a directory |
|
|
197
|
-
| `ensureDir` | `ensureDir(fullKeyBuilder: (dir: string) => string, dirPath: string): Promise<void>` | Recursively ensure a directory path exists |
|
|
198
|
-
|
|
199
|
-
## Usage
|
|
200
|
-
|
|
201
|
-
```ts
|
|
202
|
-
import "@simplysm/core-browser"; // activate prototype extensions
|
|
130
|
+
### IndexedDB Store
|
|
203
131
|
|
|
204
|
-
|
|
205
|
-
const el = document.querySelector(".my-element")!;
|
|
206
|
-
const parents = el.getParents();
|
|
207
|
-
const child = el.findFirst<HTMLDivElement>(".child");
|
|
208
|
-
|
|
209
|
-
// File download
|
|
210
|
-
import { downloadBlob, fetchUrlBytes } from "@simplysm/core-browser";
|
|
211
|
-
|
|
212
|
-
const bytes = await fetchUrlBytes("/api/data.bin", {
|
|
213
|
-
onProgress: (p) => console.log(`${p.receivedLength}/${p.contentLength}`),
|
|
214
|
-
});
|
|
215
|
-
downloadBlob(new Blob([bytes]), "data.bin");
|
|
216
|
-
|
|
217
|
-
// IndexedDB store
|
|
132
|
+
```typescript
|
|
218
133
|
import { IndexedDbStore } from "@simplysm/core-browser";
|
|
219
134
|
|
|
220
135
|
const store = new IndexedDbStore("myDb", 1, [{ name: "items", keyPath: "id" }]);
|
|
221
136
|
await store.put("items", { id: "1", value: "hello" });
|
|
222
|
-
const item = await store.get("items", "1");
|
|
137
|
+
const item = await store.get<{ id: string; value: string }>("items", "1");
|
|
223
138
|
store.close();
|
|
224
139
|
```
|
|
140
|
+
|
|
141
|
+
### Get element bounds
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { getBounds } from "@simplysm/core-browser";
|
|
145
|
+
|
|
146
|
+
const elements = document.querySelectorAll(".card");
|
|
147
|
+
const bounds = await getBounds([...elements]);
|
|
148
|
+
for (const b of bounds) {
|
|
149
|
+
console.log(`${b.target.tagName}: ${b.top}, ${b.left}, ${b.width}x${b.height}`);
|
|
150
|
+
}
|
|
151
|
+
```
|
package/docs/download.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Download
|
|
2
|
+
|
|
3
|
+
Utility for triggering file downloads in the browser.
|
|
4
|
+
|
|
5
|
+
## `downloadBlob`
|
|
6
|
+
|
|
7
|
+
Download a `Blob` as a file by creating a temporary object URL and clicking a hidden anchor element. The object URL is revoked after 1 second.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
function downloadBlob(blob: Blob, fileName: string): void
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Description |
|
|
14
|
+
|-----------|------|-------------|
|
|
15
|
+
| `blob` | `Blob` | The Blob to download |
|
|
16
|
+
| `fileName` | `string` | File name for the download |
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Element Extensions
|
|
2
|
+
|
|
3
|
+
Side-effect import that extends the global `Element` prototype with DOM utility methods, plus standalone clipboard and bounds functions.
|
|
4
|
+
|
|
5
|
+
## `ElementBounds`
|
|
6
|
+
|
|
7
|
+
Bounding rectangle information for an element, returned by `getBounds`.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface ElementBounds {
|
|
11
|
+
target: Element;
|
|
12
|
+
top: number;
|
|
13
|
+
left: number;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| Field | Type | Description |
|
|
20
|
+
|-------|------|-------------|
|
|
21
|
+
| `target` | `Element` | The measured element |
|
|
22
|
+
| `top` | `number` | Top position relative to viewport |
|
|
23
|
+
| `left` | `number` | Left position relative to viewport |
|
|
24
|
+
| `width` | `number` | Element width |
|
|
25
|
+
| `height` | `number` | Element height |
|
|
26
|
+
|
|
27
|
+
## `Element.prototype.findAll`
|
|
28
|
+
|
|
29
|
+
Find all descendant elements matching a CSS selector.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
findAll<TEl extends Element = Element>(selector: string): TEl[]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Parameter | Type | Description |
|
|
36
|
+
|-----------|------|-------------|
|
|
37
|
+
| `selector` | `string` | CSS selector. Returns empty array if empty string. |
|
|
38
|
+
|
|
39
|
+
**Returns:** `TEl[]` -- Array of matching elements.
|
|
40
|
+
|
|
41
|
+
## `Element.prototype.findFirst`
|
|
42
|
+
|
|
43
|
+
Find the first descendant element matching a CSS selector.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
findFirst<TEl extends Element = Element>(selector: string): TEl | undefined
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
| Parameter | Type | Description |
|
|
50
|
+
|-----------|------|-------------|
|
|
51
|
+
| `selector` | `string` | CSS selector. Returns `undefined` if empty string. |
|
|
52
|
+
|
|
53
|
+
**Returns:** `TEl | undefined` -- First matching element or `undefined`.
|
|
54
|
+
|
|
55
|
+
## `Element.prototype.prependChild`
|
|
56
|
+
|
|
57
|
+
Insert a child element as the first child.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
prependChild<TEl extends Element>(child: TEl): TEl
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Parameter | Type | Description |
|
|
64
|
+
|-----------|------|-------------|
|
|
65
|
+
| `child` | `TEl` | Element to insert |
|
|
66
|
+
|
|
67
|
+
**Returns:** `TEl` -- The inserted child element.
|
|
68
|
+
|
|
69
|
+
## `Element.prototype.getParents`
|
|
70
|
+
|
|
71
|
+
Get all ancestor elements, ordered from nearest to farthest.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
getParents(): Element[]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Returns:** `Element[]` -- Array of parent elements (nearest first).
|
|
78
|
+
|
|
79
|
+
## `Element.prototype.findFocusableParent`
|
|
80
|
+
|
|
81
|
+
Find the nearest focusable ancestor element (uses `tabbable` library).
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
findFocusableParent(): HTMLElement | undefined
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Returns:** `HTMLElement | undefined` -- First focusable parent or `undefined`.
|
|
88
|
+
|
|
89
|
+
## `Element.prototype.findFirstFocusableChild`
|
|
90
|
+
|
|
91
|
+
Find the first focusable descendant element (uses `tabbable` library).
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
findFirstFocusableChild(): HTMLElement | undefined
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Returns:** `HTMLElement | undefined` -- First focusable child or `undefined`.
|
|
98
|
+
|
|
99
|
+
## `Element.prototype.isOffsetElement`
|
|
100
|
+
|
|
101
|
+
Check whether the element is an offset parent (`position: relative | absolute | fixed | sticky`).
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
isOffsetElement(): boolean
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Returns:** `boolean` -- `true` if element has a positioning CSS property.
|
|
108
|
+
|
|
109
|
+
## `Element.prototype.isVisible`
|
|
110
|
+
|
|
111
|
+
Check whether the element is visible on screen. Checks `getClientRects()`, `visibility`, and `opacity`.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
isVisible(): boolean
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Returns:** `boolean` -- `true` if the element is visible.
|
|
118
|
+
|
|
119
|
+
## `copyElement`
|
|
120
|
+
|
|
121
|
+
Copy element content to the clipboard. Intended for use as a `copy` event handler. Finds the first `input` or `textarea` within the event target and copies its value.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
function copyElement(event: ClipboardEvent): void
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| Parameter | Type | Description |
|
|
128
|
+
|-----------|------|-------------|
|
|
129
|
+
| `event` | `ClipboardEvent` | The copy event object |
|
|
130
|
+
|
|
131
|
+
## `pasteToElement`
|
|
132
|
+
|
|
133
|
+
Paste clipboard content into an element. Intended for use as a `paste` event handler. Finds the first `input` or `textarea` within the event target and replaces its entire value with the clipboard text.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
function pasteToElement(event: ClipboardEvent): void
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
| Parameter | Type | Description |
|
|
140
|
+
|-----------|------|-------------|
|
|
141
|
+
| `event` | `ClipboardEvent` | The paste event object |
|
|
142
|
+
|
|
143
|
+
## `getBounds`
|
|
144
|
+
|
|
145
|
+
Get bounding rectangle information for multiple elements using `IntersectionObserver`. Results are returned in the same order as the input array. Duplicate elements are deduplicated.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
async function getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
| Parameter | Type | Default | Description |
|
|
152
|
+
|-----------|------|---------|-------------|
|
|
153
|
+
| `els` | `Element[]` | | Target elements |
|
|
154
|
+
| `timeout` | `number` | `5000` | Timeout in milliseconds |
|
|
155
|
+
|
|
156
|
+
**Returns:** `Promise<ElementBounds[]>` -- Bounding info sorted by input order.
|
|
157
|
+
|
|
158
|
+
**Throws:** `TimeoutError` if the observer does not respond within the timeout.
|
package/docs/fetch.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Fetch
|
|
2
|
+
|
|
3
|
+
Utility for downloading binary data from a URL with optional progress tracking.
|
|
4
|
+
|
|
5
|
+
## `DownloadProgress`
|
|
6
|
+
|
|
7
|
+
Progress information emitted during a fetch download.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface DownloadProgress {
|
|
11
|
+
receivedLength: number;
|
|
12
|
+
contentLength: number;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Field | Type | Description |
|
|
17
|
+
|-------|------|-------------|
|
|
18
|
+
| `receivedLength` | `number` | Bytes received so far |
|
|
19
|
+
| `contentLength` | `number` | Total content length from the `Content-Length` header |
|
|
20
|
+
|
|
21
|
+
## `fetchUrlBytes`
|
|
22
|
+
|
|
23
|
+
Download binary data from a URL. Supports progress callbacks via `ReadableStream` reader. When the `Content-Length` header is available, the result buffer is pre-allocated for efficiency. When unavailable (chunked encoding), chunks are collected and concatenated.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
async function fetchUrlBytes(
|
|
27
|
+
url: string,
|
|
28
|
+
options?: { onProgress?: (progress: DownloadProgress) => void },
|
|
29
|
+
): Promise<Uint8Array>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Parameter | Type | Description |
|
|
33
|
+
|-----------|------|-------------|
|
|
34
|
+
| `url` | `string` | URL to fetch |
|
|
35
|
+
| `options.onProgress` | `(progress: DownloadProgress) => void` | Optional progress callback |
|
|
36
|
+
|
|
37
|
+
**Returns:** `Promise<Uint8Array>` -- The downloaded binary data.
|
|
38
|
+
|
|
39
|
+
**Throws:** `Error` if the response is not OK or the body cannot be read.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# File Dialog
|
|
2
|
+
|
|
3
|
+
Utility for programmatically opening a native file picker dialog.
|
|
4
|
+
|
|
5
|
+
## `openFileDialog`
|
|
6
|
+
|
|
7
|
+
Open a file selection dialog by creating and clicking a hidden `<input type="file">` element. Returns the selected files or `undefined` if the dialog is cancelled.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
function openFileDialog(options?: {
|
|
11
|
+
accept?: string;
|
|
12
|
+
multiple?: boolean;
|
|
13
|
+
}): Promise<File[] | undefined>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Parameter | Type | Default | Description |
|
|
17
|
+
|-----------|------|---------|-------------|
|
|
18
|
+
| `options.accept` | `string` | `undefined` | File type filter (e.g., `".csv"`, `"image/*"`) |
|
|
19
|
+
| `options.multiple` | `boolean` | `false` | Allow selecting multiple files |
|
|
20
|
+
|
|
21
|
+
**Returns:** `Promise<File[] | undefined>` -- Selected files array, or `undefined` if cancelled or no files selected.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# HTML Element Extensions
|
|
2
|
+
|
|
3
|
+
Side-effect import that extends the global `HTMLElement` prototype with layout and scroll utility methods.
|
|
4
|
+
|
|
5
|
+
## `HTMLElement.prototype.repaint`
|
|
6
|
+
|
|
7
|
+
Force a synchronous repaint by triggering a reflow (reads `offsetHeight`).
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
repaint(): void
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## `HTMLElement.prototype.getRelativeOffset`
|
|
14
|
+
|
|
15
|
+
Calculate the element's position relative to a parent element. Returns document-based coordinates (including `window.scrollX/Y`) suitable for CSS `top`/`left` properties.
|
|
16
|
+
|
|
17
|
+
The calculation accounts for:
|
|
18
|
+
- Viewport position (`getBoundingClientRect`)
|
|
19
|
+
- Document scroll (`window.scrollX/Y`)
|
|
20
|
+
- Parent internal scroll (`parentEl.scrollTop/Left`)
|
|
21
|
+
- Intermediate element border widths
|
|
22
|
+
- CSS `transform`
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
getRelativeOffset(parent: HTMLElement | string): { top: number; left: number }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| Parameter | Type | Description |
|
|
29
|
+
|-----------|------|-------------|
|
|
30
|
+
| `parent` | `HTMLElement \| string` | Parent element or CSS selector to match via `closest()` |
|
|
31
|
+
|
|
32
|
+
**Returns:** `{ top: number; left: number }` -- Coordinates usable for CSS `top`/`left`.
|
|
33
|
+
|
|
34
|
+
**Throws:** `ArgumentError` if the parent element cannot be found.
|
|
35
|
+
|
|
36
|
+
## `HTMLElement.prototype.scrollIntoViewIfNeeded`
|
|
37
|
+
|
|
38
|
+
Scroll the container so that the target position is not obscured by fixed offset areas (e.g., sticky headers or fixed columns). Only handles cases where the target is above or to the left of the visible area; downward/rightward scrolling relies on the browser's default focus scroll behavior.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
scrollIntoViewIfNeeded(
|
|
42
|
+
target: { top: number; left: number },
|
|
43
|
+
offset?: { top: number; left: number },
|
|
44
|
+
): void
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Default | Description |
|
|
48
|
+
|-----------|------|---------|-------------|
|
|
49
|
+
| `target` | `{ top: number; left: number }` | | Target position within the container (`offsetTop`, `offsetLeft`) |
|
|
50
|
+
| `offset` | `{ top: number; left: number }` | `{ top: 0, left: 0 }` | Size of the offset area that must not obscure the target (e.g., fixed header height) |
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# IndexedDB Store
|
|
2
|
+
|
|
3
|
+
Generic IndexedDB wrapper providing typed CRUD operations and transaction management.
|
|
4
|
+
|
|
5
|
+
## `StoreConfig`
|
|
6
|
+
|
|
7
|
+
Configuration for an IndexedDB object store, used when opening the database.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface StoreConfig {
|
|
11
|
+
name: string;
|
|
12
|
+
keyPath: string;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Field | Type | Description |
|
|
17
|
+
|-------|------|-------------|
|
|
18
|
+
| `name` | `string` | Object store name |
|
|
19
|
+
| `keyPath` | `string` | Key path for the object store |
|
|
20
|
+
|
|
21
|
+
## `IndexedDbStore`
|
|
22
|
+
|
|
23
|
+
A wrapper around the IndexedDB API with automatic database opening, version upgrade handling, and typed CRUD methods.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
class IndexedDbStore {
|
|
27
|
+
constructor(dbName: string, dbVersion: number, storeConfigs: StoreConfig[]);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Constructor Parameter | Type | Description |
|
|
32
|
+
|----------------------|------|-------------|
|
|
33
|
+
| `dbName` | `string` | Database name |
|
|
34
|
+
| `dbVersion` | `number` | Database version (triggers `onupgradeneeded` when increased) |
|
|
35
|
+
| `storeConfigs` | `StoreConfig[]` | Object store configurations to create on upgrade |
|
|
36
|
+
|
|
37
|
+
### Methods
|
|
38
|
+
|
|
39
|
+
#### `open`
|
|
40
|
+
|
|
41
|
+
Open the database (creates stores on version upgrade). Returns the existing connection if already open. Concurrent calls return the same pending promise.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
async open(): Promise<IDBDatabase>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### `withStore`
|
|
48
|
+
|
|
49
|
+
Execute a function within a transaction on a specific store. Handles transaction commit/abort automatically.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
async withStore<TResult>(
|
|
53
|
+
storeName: string,
|
|
54
|
+
mode: IDBTransactionMode,
|
|
55
|
+
fn: (store: IDBObjectStore) => Promise<TResult>,
|
|
56
|
+
): Promise<TResult>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Description |
|
|
60
|
+
|-----------|------|-------------|
|
|
61
|
+
| `storeName` | `string` | Target object store name |
|
|
62
|
+
| `mode` | `IDBTransactionMode` | `"readonly"` or `"readwrite"` |
|
|
63
|
+
| `fn` | `(store: IDBObjectStore) => Promise<TResult>` | Function to execute within the transaction |
|
|
64
|
+
|
|
65
|
+
#### `get`
|
|
66
|
+
|
|
67
|
+
Get a single value by key.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
async get<TValue>(storeName: string, key: IDBValidKey): Promise<TValue | undefined>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### `put`
|
|
74
|
+
|
|
75
|
+
Insert or update a value.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
async put(storeName: string, value: unknown): Promise<void>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### `delete`
|
|
82
|
+
|
|
83
|
+
Delete a value by key.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
async delete(storeName: string, key: IDBValidKey): Promise<void>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `getAll`
|
|
90
|
+
|
|
91
|
+
Get all values from a store.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
async getAll<TItem>(storeName: string): Promise<TItem[]>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### `close`
|
|
98
|
+
|
|
99
|
+
Close the database connection.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
close(): void
|
|
103
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# IndexedDB Virtual FS
|
|
2
|
+
|
|
3
|
+
A virtual file system abstraction backed by IndexedDB, built on top of `IndexedDbStore`.
|
|
4
|
+
|
|
5
|
+
## `VirtualFsEntry`
|
|
6
|
+
|
|
7
|
+
Represents a file or directory entry in the virtual file system.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface VirtualFsEntry {
|
|
11
|
+
kind: "file" | "dir";
|
|
12
|
+
dataBase64?: string;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Field | Type | Description |
|
|
17
|
+
|-------|------|-------------|
|
|
18
|
+
| `kind` | `"file" \| "dir"` | Entry type |
|
|
19
|
+
| `dataBase64` | `string \| undefined` | Base64-encoded file data (only for files) |
|
|
20
|
+
|
|
21
|
+
## `IndexedDbVirtualFs`
|
|
22
|
+
|
|
23
|
+
Virtual file system that stores entries in an `IndexedDbStore`. Supports hierarchical path-based operations including directory creation, prefix-based deletion, and child listing.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
class IndexedDbVirtualFs {
|
|
27
|
+
constructor(db: IndexedDbStore, storeName: string, keyField: string);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Constructor Parameter | Type | Description |
|
|
32
|
+
|----------------------|------|-------------|
|
|
33
|
+
| `db` | `IndexedDbStore` | The IndexedDB store instance |
|
|
34
|
+
| `storeName` | `string` | Object store name to use |
|
|
35
|
+
| `keyField` | `string` | Field name used as the key in the store |
|
|
36
|
+
|
|
37
|
+
### Methods
|
|
38
|
+
|
|
39
|
+
#### `getEntry`
|
|
40
|
+
|
|
41
|
+
Get a virtual FS entry by its full key.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
async getEntry(fullKey: string): Promise<VirtualFsEntry | undefined>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Description |
|
|
48
|
+
|-----------|------|-------------|
|
|
49
|
+
| `fullKey` | `string` | Full key identifying the entry |
|
|
50
|
+
|
|
51
|
+
#### `putEntry`
|
|
52
|
+
|
|
53
|
+
Insert or update a virtual FS entry.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
async putEntry(fullKey: string, kind: "file" | "dir", dataBase64?: string): Promise<void>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Description |
|
|
60
|
+
|-----------|------|-------------|
|
|
61
|
+
| `fullKey` | `string` | Full key for the entry |
|
|
62
|
+
| `kind` | `"file" \| "dir"` | Entry type |
|
|
63
|
+
| `dataBase64` | `string` | Optional base64-encoded data (for files) |
|
|
64
|
+
|
|
65
|
+
#### `deleteByPrefix`
|
|
66
|
+
|
|
67
|
+
Delete all entries whose key matches the given prefix or starts with `prefix + "/"`.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
async deleteByPrefix(keyPrefix: string): Promise<boolean>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Parameter | Type | Description |
|
|
74
|
+
|-----------|------|-------------|
|
|
75
|
+
| `keyPrefix` | `string` | Key prefix to match |
|
|
76
|
+
|
|
77
|
+
**Returns:** `Promise<boolean>` -- `true` if at least one entry was deleted.
|
|
78
|
+
|
|
79
|
+
#### `listChildren`
|
|
80
|
+
|
|
81
|
+
List immediate children under a given prefix. Returns each child's name and whether it is a directory.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
async listChildren(prefix: string): Promise<{ name: string; isDirectory: boolean }[]>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
| Parameter | Type | Description |
|
|
88
|
+
|-----------|------|-------------|
|
|
89
|
+
| `prefix` | `string` | Parent path prefix (e.g., `"/root/dir/"`) |
|
|
90
|
+
|
|
91
|
+
**Returns:** `Promise<{ name: string; isDirectory: boolean }[]>` -- Array of child entries.
|
|
92
|
+
|
|
93
|
+
#### `ensureDir`
|
|
94
|
+
|
|
95
|
+
Ensure all directories along a path exist, creating missing ones.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
async ensureDir(
|
|
99
|
+
fullKeyBuilder: (path: string) => string,
|
|
100
|
+
dirPath: string,
|
|
101
|
+
): Promise<void>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Parameter | Type | Description |
|
|
105
|
+
|-----------|------|-------------|
|
|
106
|
+
| `fullKeyBuilder` | `(path: string) => string` | Function to build the full key from a path segment |
|
|
107
|
+
| `dirPath` | `string` | Directory path to ensure (e.g., `"/a/b/c"`) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/core-browser",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.6",
|
|
4
4
|
"description": "심플리즘 패키지 - 코어 (browser)",
|
|
5
5
|
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
-
"src"
|
|
17
|
+
"src",
|
|
18
|
+
"docs"
|
|
18
19
|
],
|
|
19
20
|
"sideEffects": [
|
|
20
21
|
"./src/extensions/element-ext.ts",
|
|
@@ -24,6 +25,6 @@
|
|
|
24
25
|
],
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"tabbable": "^6.4.0",
|
|
27
|
-
"@simplysm/core-common": "14.0.
|
|
28
|
+
"@simplysm/core-common": "14.0.6"
|
|
28
29
|
}
|
|
29
30
|
}
|