@simplysm/core-browser 13.0.0-beta.6 → 13.0.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 (107) hide show
  1. package/dist/extensions/element-ext.d.ts.map +1 -0
  2. package/dist/extensions/element-ext.js.map +0 -1
  3. package/dist/extensions/html-element-ext.d.ts.map +1 -0
  4. package/dist/extensions/html-element-ext.js.map +0 -1
  5. package/dist/{core-browser/src/index.d.ts → index.d.ts} +2 -1
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +7 -6
  8. package/dist/index.js.map +1 -2
  9. package/dist/utils/download.d.ts +8 -0
  10. package/dist/utils/download.d.ts.map +1 -0
  11. package/dist/utils/download.js +8 -40
  12. package/dist/utils/download.js.map +2 -3
  13. package/dist/{core-browser/src/utils/download.d.ts → utils/fetch.d.ts} +2 -2
  14. package/dist/utils/fetch.d.ts.map +1 -0
  15. package/dist/utils/fetch.js +47 -0
  16. package/dist/utils/fetch.js.map +6 -0
  17. package/dist/utils/file-dialog.d.ts +8 -0
  18. package/dist/utils/file-dialog.d.ts.map +1 -0
  19. package/dist/utils/file-dialog.js +18 -0
  20. package/dist/utils/file-dialog.js.map +6 -0
  21. package/package.json +9 -5
  22. package/src/index.ts +2 -1
  23. package/src/utils/download.ts +11 -60
  24. package/src/utils/fetch.ts +66 -0
  25. package/src/utils/file-dialog.ts +17 -0
  26. package/.cache/typecheck-browser.tsbuildinfo +0 -1
  27. package/.cache/typecheck-tests-browser.tsbuildinfo +0 -1
  28. package/dist/core-browser/src/extensions/element-ext.d.ts.map +0 -1
  29. package/dist/core-browser/src/extensions/html-element-ext.d.ts.map +0 -1
  30. package/dist/core-browser/src/index.d.ts.map +0 -1
  31. package/dist/core-browser/src/utils/blob.d.ts +0 -10
  32. package/dist/core-browser/src/utils/blob.d.ts.map +0 -1
  33. package/dist/core-browser/src/utils/download.d.ts.map +0 -1
  34. package/dist/core-common/src/common.types.d.ts +0 -74
  35. package/dist/core-common/src/common.types.d.ts.map +0 -1
  36. package/dist/core-common/src/env.d.ts +0 -6
  37. package/dist/core-common/src/env.d.ts.map +0 -1
  38. package/dist/core-common/src/errors/argument-error.d.ts +0 -25
  39. package/dist/core-common/src/errors/argument-error.d.ts.map +0 -1
  40. package/dist/core-common/src/errors/not-implemented-error.d.ts +0 -29
  41. package/dist/core-common/src/errors/not-implemented-error.d.ts.map +0 -1
  42. package/dist/core-common/src/errors/sd-error.d.ts +0 -27
  43. package/dist/core-common/src/errors/sd-error.d.ts.map +0 -1
  44. package/dist/core-common/src/errors/timeout-error.d.ts +0 -31
  45. package/dist/core-common/src/errors/timeout-error.d.ts.map +0 -1
  46. package/dist/core-common/src/extensions/arr-ext.d.ts +0 -15
  47. package/dist/core-common/src/extensions/arr-ext.d.ts.map +0 -1
  48. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts +0 -19
  49. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts.map +0 -1
  50. package/dist/core-common/src/extensions/arr-ext.types.d.ts +0 -215
  51. package/dist/core-common/src/extensions/arr-ext.types.d.ts.map +0 -1
  52. package/dist/core-common/src/extensions/map-ext.d.ts +0 -57
  53. package/dist/core-common/src/extensions/map-ext.d.ts.map +0 -1
  54. package/dist/core-common/src/extensions/set-ext.d.ts +0 -36
  55. package/dist/core-common/src/extensions/set-ext.d.ts.map +0 -1
  56. package/dist/core-common/src/features/debounce-queue.d.ts +0 -53
  57. package/dist/core-common/src/features/debounce-queue.d.ts.map +0 -1
  58. package/dist/core-common/src/features/event-emitter.d.ts +0 -66
  59. package/dist/core-common/src/features/event-emitter.d.ts.map +0 -1
  60. package/dist/core-common/src/features/serial-queue.d.ts +0 -47
  61. package/dist/core-common/src/features/serial-queue.d.ts.map +0 -1
  62. package/dist/core-common/src/index.d.ts +0 -32
  63. package/dist/core-common/src/index.d.ts.map +0 -1
  64. package/dist/core-common/src/types/date-only.d.ts +0 -152
  65. package/dist/core-common/src/types/date-only.d.ts.map +0 -1
  66. package/dist/core-common/src/types/date-time.d.ts +0 -96
  67. package/dist/core-common/src/types/date-time.d.ts.map +0 -1
  68. package/dist/core-common/src/types/lazy-gc-map.d.ts +0 -80
  69. package/dist/core-common/src/types/lazy-gc-map.d.ts.map +0 -1
  70. package/dist/core-common/src/types/time.d.ts +0 -68
  71. package/dist/core-common/src/types/time.d.ts.map +0 -1
  72. package/dist/core-common/src/types/uuid.d.ts +0 -35
  73. package/dist/core-common/src/types/uuid.d.ts.map +0 -1
  74. package/dist/core-common/src/utils/bytes.d.ts +0 -51
  75. package/dist/core-common/src/utils/bytes.d.ts.map +0 -1
  76. package/dist/core-common/src/utils/date-format.d.ts +0 -90
  77. package/dist/core-common/src/utils/date-format.d.ts.map +0 -1
  78. package/dist/core-common/src/utils/json.d.ts +0 -34
  79. package/dist/core-common/src/utils/json.d.ts.map +0 -1
  80. package/dist/core-common/src/utils/num.d.ts +0 -60
  81. package/dist/core-common/src/utils/num.d.ts.map +0 -1
  82. package/dist/core-common/src/utils/obj.d.ts +0 -258
  83. package/dist/core-common/src/utils/obj.d.ts.map +0 -1
  84. package/dist/core-common/src/utils/path.d.ts +0 -23
  85. package/dist/core-common/src/utils/path.d.ts.map +0 -1
  86. package/dist/core-common/src/utils/primitive.d.ts +0 -18
  87. package/dist/core-common/src/utils/primitive.d.ts.map +0 -1
  88. package/dist/core-common/src/utils/str.d.ts +0 -103
  89. package/dist/core-common/src/utils/str.d.ts.map +0 -1
  90. package/dist/core-common/src/utils/template-strings.d.ts +0 -84
  91. package/dist/core-common/src/utils/template-strings.d.ts.map +0 -1
  92. package/dist/core-common/src/utils/transferable.d.ts +0 -47
  93. package/dist/core-common/src/utils/transferable.d.ts.map +0 -1
  94. package/dist/core-common/src/utils/wait.d.ts +0 -19
  95. package/dist/core-common/src/utils/wait.d.ts.map +0 -1
  96. package/dist/core-common/src/utils/xml.d.ts +0 -36
  97. package/dist/core-common/src/utils/xml.d.ts.map +0 -1
  98. package/dist/core-common/src/zip/sd-zip.d.ts +0 -80
  99. package/dist/core-common/src/zip/sd-zip.d.ts.map +0 -1
  100. package/dist/utils/blob.js +0 -19
  101. package/dist/utils/blob.js.map +0 -7
  102. package/src/utils/blob.ts +0 -19
  103. package/tests/extensions/element-ext.spec.ts +0 -729
  104. package/tests/extensions/html-element-ext.spec.ts +0 -190
  105. package/tests/utils/blob.spec.ts +0 -68
  106. /package/dist/{core-browser/src/extensions → extensions}/element-ext.d.ts +0 -0
  107. /package/dist/{core-browser/src/extensions → extensions}/html-element-ext.d.ts +0 -0
@@ -0,0 +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;AAgED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAUvD;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,CAkDhG"}
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/extensions/element-ext.ts"],
4
- "sourcesContent": ["import { isFocusable } from \"tabbable\";\nimport { TimeoutError } from \"@simplysm/core-common\";\n\n/**\n * \uC694\uC18C bounds \uC815\uBCF4 \uD0C0\uC785\n */\nexport interface ElementBounds {\n /** \uCE21\uC815 \uB300\uC0C1 \uC694\uC18C */\n target: Element;\n /** \uBDF0\uD3EC\uD2B8 \uAE30\uC900 \uC0C1\uB2E8 \uC704\uCE58 */\n top: number;\n /** \uBDF0\uD3EC\uD2B8 \uAE30\uC900 \uC67C\uCABD \uC704\uCE58 */\n left: number;\n /** \uC694\uC18C \uB108\uBE44 */\n width: number;\n /** \uC694\uC18C \uB192\uC774 */\n height: number;\n}\n\ndeclare global {\n interface Element {\n /**\n * \uC140\uB809\uD130\uB85C \uD558\uC704 \uC694\uC18C \uC804\uCCB4 \uAC80\uC0C9\n *\n * @param selector - CSS \uC140\uB809\uD130\n * @returns \uB9E4\uCE6D\uB41C \uC694\uC18C \uBC30\uC5F4 (\uBE48 \uC140\uB809\uD130\uB294 \uBE48 \uBC30\uC5F4 \uBC18\uD658)\n */\n findAll<T extends Element = Element>(selector: string): T[];\n\n /**\n * \uC140\uB809\uD130\uB85C \uCCAB \uBC88\uC9F8 \uB9E4\uCE6D \uC694\uC18C \uAC80\uC0C9\n *\n * @param selector - CSS \uC140\uB809\uD130\n * @returns \uCCAB \uBC88\uC9F8 \uB9E4\uCE6D \uC694\uC18C \uB610\uB294 undefined (\uBE48 \uC140\uB809\uD130\uB294 undefined \uBC18\uD658)\n */\n findFirst<T extends Element = Element>(selector: string): T | undefined;\n\n /**\n * \uC694\uC18C\uB97C \uCCAB \uBC88\uC9F8 \uC790\uC2DD\uC73C\uB85C \uC0BD\uC785\n *\n * @param child - \uC0BD\uC785\uD560 \uC790\uC2DD \uC694\uC18C\n * @returns \uC0BD\uC785\uB41C \uC790\uC2DD \uC694\uC18C\n */\n prependChild<T extends Element>(child: T): T;\n\n /**\n * \uBAA8\uB4E0 \uBD80\uBAA8 \uC694\uC18C \uBAA9\uB85D \uBC18\uD658 (\uAC00\uAE4C\uC6B4 \uC21C\uC11C)\n *\n * @returns \uBD80\uBAA8 \uC694\uC18C \uBC30\uC5F4 (\uAC00\uAE4C\uC6B4 \uBD80\uBAA8\uBD80\uD130 \uC21C\uC11C\uB300\uB85C)\n */\n getParents(): Element[];\n\n /**\n * \uBD80\uBAA8 \uC911 \uCCAB \uBC88\uC9F8 \uD3EC\uCEE4\uC2A4 \uAC00\uB2A5 \uC694\uC18C \uAC80\uC0C9 (tabbable \uC0AC\uC6A9)\n *\n * @returns \uD3EC\uCEE4\uC2A4 \uAC00\uB2A5\uD55C \uCCAB \uBC88\uC9F8 \uBD80\uBAA8 \uC694\uC18C \uB610\uB294 undefined\n */\n findFocusableParent(): HTMLElement | undefined;\n\n /**\n * \uC790\uC2DD \uC911 \uCCAB \uBC88\uC9F8 \uD3EC\uCEE4\uC2A4 \uAC00\uB2A5 \uC694\uC18C \uAC80\uC0C9 (tabbable \uC0AC\uC6A9)\n *\n * @returns \uD3EC\uCEE4\uC2A4 \uAC00\uB2A5\uD55C \uCCAB \uBC88\uC9F8 \uC790\uC2DD \uC694\uC18C \uB610\uB294 undefined\n */\n findFirstFocusableChild(): HTMLElement | undefined;\n\n /**\n * \uC694\uC18C\uAC00 offset \uAE30\uC900 \uC694\uC18C\uC778\uC9C0 \uD655\uC778 (position: relative/absolute/fixed/sticky)\n *\n * @returns position \uC18D\uC131\uC774 relative, absolute, fixed, sticky \uC911 \uD558\uB098\uBA74 true\n */\n isOffsetElement(): boolean;\n\n /**\n * \uC694\uC18C\uAC00 \uD654\uBA74\uC5D0 \uBCF4\uC774\uB294\uC9C0 \uD655\uC778\n *\n * @remarks\n * clientRects \uC874\uC7AC \uC5EC\uBD80, visibility: hidden, opacity: 0 \uC5EC\uBD80\uB97C \uD655\uC778\uD55C\uB2E4.\n *\n * @returns \uC694\uC18C\uAC00 \uD654\uBA74\uC5D0 \uBCF4\uC774\uBA74 true\n */\n isVisible(): boolean;\n }\n}\n\nElement.prototype.findAll = function <T extends Element = Element>(selector: string): T[] {\n const trimmed = selector.trim();\n if (trimmed === \"\") return [];\n return Array.from(this.querySelectorAll<T>(trimmed));\n};\n\nElement.prototype.findFirst = function <T extends Element = Element>(selector: string): T | undefined {\n const trimmed = selector.trim();\n if (trimmed === \"\") return undefined;\n return this.querySelector<T>(trimmed) ?? undefined;\n};\n\nElement.prototype.prependChild = function <T extends Element>(child: T): T {\n return this.insertBefore(child, this.firstElementChild);\n};\n\nElement.prototype.getParents = function (): Element[] {\n const result: Element[] = [];\n let cursor = this.parentNode;\n while (cursor !== null && cursor instanceof Element) {\n result.push(cursor);\n cursor = cursor.parentNode;\n }\n return result;\n};\n\nElement.prototype.findFocusableParent = function (): HTMLElement | undefined {\n let parentEl = this.parentElement;\n while (parentEl !== null) {\n if (isFocusable(parentEl)) {\n return parentEl;\n }\n parentEl = parentEl.parentElement;\n }\n return undefined;\n};\n\nElement.prototype.findFirstFocusableChild = function (): HTMLElement | undefined {\n const walker = document.createTreeWalker(this, NodeFilter.SHOW_ELEMENT);\n let node = walker.nextNode();\n while (node !== null) {\n if (node instanceof HTMLElement && isFocusable(node)) {\n return node;\n }\n node = walker.nextNode();\n }\n return undefined;\n};\n\nElement.prototype.isOffsetElement = function (): boolean {\n return [\"relative\", \"absolute\", \"fixed\", \"sticky\"].includes(getComputedStyle(this).position);\n};\n\nElement.prototype.isVisible = function (): boolean {\n const style = getComputedStyle(this);\n return this.getClientRects().length > 0 && style.visibility !== \"hidden\" && style.opacity !== \"0\";\n};\n\n// ============================================================================\n// \uC815\uC801 \uD568\uC218 (\uC774\uBCA4\uD2B8 \uD578\uB4E4\uB7EC\uC6A9 \uB610\uB294 \uC5EC\uB7EC \uC694\uC18C \uB300\uC0C1)\n// ============================================================================\n\n/**\n * \uC694\uC18C \uB0B4\uC6A9\uC744 \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC (copy \uC774\uBCA4\uD2B8 \uD578\uB4E4\uB7EC\uC5D0\uC11C \uC0AC\uC6A9)\n *\n * @param event - copy \uC774\uBCA4\uD2B8 \uAC1D\uCCB4\n */\nexport function copyElement(event: ClipboardEvent): void {\n const clipboardData = event.clipboardData;\n const target = event.target;\n if (clipboardData == null || !(target instanceof Element)) return;\n\n const firstInputEl = target.querySelector<HTMLInputElement | HTMLTextAreaElement>(\"input, textarea\");\n if (firstInputEl != null) {\n clipboardData.setData(\"text/plain\", firstInputEl.value);\n event.preventDefault();\n }\n}\n\n/**\n * \uD074\uB9BD\uBCF4\uB4DC \uB0B4\uC6A9\uC744 \uC694\uC18C\uC5D0 \uBD99\uC5EC\uB123\uAE30 (paste \uC774\uBCA4\uD2B8 \uD578\uB4E4\uB7EC\uC5D0\uC11C \uC0AC\uC6A9)\n *\n * @remarks\n * \uB300\uC0C1 \uC694\uC18C \uB0B4\uC758 \uCCAB \uBC88\uC9F8 input/textarea\uB97C \uCC3E\uC544 \uC804\uCCB4 \uAC12\uC744 \uD074\uB9BD\uBCF4\uB4DC \uB0B4\uC6A9\uC73C\uB85C \uAD50\uCCB4\uD55C\uB2E4.\n * \uCEE4\uC11C \uC704\uCE58\uB098 \uC120\uD0DD \uC601\uC5ED\uC744 \uACE0\uB824\uD558\uC9C0 \uC54A\uB294\uB2E4.\n *\n * @param event - paste \uC774\uBCA4\uD2B8 \uAC1D\uCCB4\n */\nexport function pasteToElement(event: ClipboardEvent): void {\n const clipboardData = event.clipboardData;\n const target = event.target;\n if (clipboardData == null || !(target instanceof Element)) return;\n\n const contentText = clipboardData.getData(\"text/plain\");\n\n const firstInputEl = target.findFirst<HTMLInputElement | HTMLTextAreaElement>(\"input, textarea\");\n if (firstInputEl !== undefined) {\n firstInputEl.value = contentText;\n firstInputEl.dispatchEvent(new Event(\"input\", { bubbles: true }));\n event.preventDefault();\n }\n}\n\n/**\n * IntersectionObserver\uB97C \uC0AC\uC6A9\uD558\uC5EC \uC694\uC18C\uB4E4\uC758 bounds \uC815\uBCF4 \uC870\uD68C\n *\n * @param els - \uB300\uC0C1 \uC694\uC18C \uBC30\uC5F4\n * @param timeout - \uD0C0\uC784\uC544\uC6C3 (\uBC00\uB9AC\uCD08, \uAE30\uBCF8: 5000)\n * @throws {TimeoutError} \uD0C0\uC784\uC544\uC6C3 \uC2DC\uAC04 \uB0B4\uC5D0 \uC751\uB2F5\uC774 \uC5C6\uC744 \uACBD\uC6B0\n */\nexport async function getBounds(els: Element[], timeout: number = 5000): Promise<ElementBounds[]> {\n // \uC911\uBCF5 \uC81C\uAC70 \uBC0F \uC785\uB825 \uC21C\uC11C\uB300\uB85C \uACB0\uACFC\uB97C \uC815\uB82C\uD558\uAE30 \uC704\uD55C \uC778\uB371\uC2A4 \uB9F5\n const indexMap = new Map(els.map((el, i) => [el, i] as const));\n if (indexMap.size === 0) {\n return [];\n }\n\n // \uC815\uB82C \uC131\uB2A5 \uCD5C\uC801\uD654\uB97C \uC704\uD55C \uC778\uB371\uC2A4 \uB9F5\n const sortIndexMap = new Map(els.map((el, i) => [el, i] as const));\n\n let observer: IntersectionObserver | undefined;\n\n try {\n return await Promise.race([\n new Promise<ElementBounds[]>((resolve) => {\n const results: ElementBounds[] = [];\n\n observer = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n const target = entry.target;\n if (indexMap.has(target)) {\n indexMap.delete(target);\n results.push({\n target,\n top: entry.boundingClientRect.top,\n left: entry.boundingClientRect.left,\n width: entry.boundingClientRect.width,\n height: entry.boundingClientRect.height,\n });\n }\n }\n\n if (indexMap.size === 0) {\n observer?.disconnect();\n // \uC785\uB825 \uC21C\uC11C\uB300\uB85C \uC815\uB82C\n resolve(results.sort((a, b) => sortIndexMap.get(a.target)! - sortIndexMap.get(b.target)!));\n }\n });\n\n for (const el of indexMap.keys()) {\n observer.observe(el);\n }\n }),\n new Promise<ElementBounds[]>((_, reject) =>\n setTimeout(() => reject(new TimeoutError(undefined, `${timeout}ms \uCD08\uACFC`)), timeout),\n ),\n ]);\n } finally {\n observer?.disconnect();\n }\n}\n"],
5
4
  "mappings": "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",
6
5
  "names": []
7
6
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-element-ext.d.ts","sourceRoot":"","sources":["../../src/extensions/html-element-ext.ts"],"names":[],"mappings":"AAEA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,WAAW;QACnB;;WAEG;QACH,OAAO,IAAI,IAAI,CAAC;QAEhB;;;;;;;;;;;;;;;;;;;;;WAqBG;QACH,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QAE/E;;;;;;;;;;WAUG;QACH,sBAAsB,CAAC,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,MAAM,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;KAC7G;CACF"}
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/extensions/html-element-ext.ts"],
4
- "sourcesContent": ["import { ArgumentError } from \"@simplysm/core-common\";\n\ndeclare global {\n interface HTMLElement {\n /**\n * \uAC15\uC81C \uB9AC\uD398\uC778\uD2B8 (reflow \uD2B8\uB9AC\uAC70)\n */\n repaint(): void;\n\n /**\n * \uBD80\uBAA8 \uC694\uC18C \uAE30\uC900 \uC0C1\uB300 \uC704\uCE58 \uACC4\uC0B0 (CSS \uD3EC\uC9C0\uC154\uB2DD\uC6A9)\n *\n * @remarks\n * \uC774 \uD568\uC218\uB294 \uC694\uC18C\uC758 \uC704\uCE58\uB97C \uBD80\uBAA8 \uC694\uC18C \uAE30\uC900\uC73C\uB85C \uACC4\uC0B0\uD558\uB418, `window.scrollX/Y`\uB97C \uD3EC\uD568\uD558\uC5EC\n * CSS `top`/`left` \uC18D\uC131\uC5D0 \uC9C1\uC811 \uC0AC\uC6A9\uD560 \uC218 \uC788\uB294 \uBB38\uC11C \uAE30\uC900 \uC88C\uD45C\uB97C \uBC18\uD658\uD55C\uB2E4.\n *\n * \uC8FC\uC694 \uC0AC\uC6A9 \uC0AC\uB840:\n * - \uB4DC\uB86D\uB2E4\uC6B4, \uD31D\uC5C5 \uB4F1\uC744 `document.body`\uC5D0 append \uD6C4 \uC704\uCE58 \uC9C0\uC815\n * - \uC2A4\uD06C\uB864\uB41C \uD398\uC774\uC9C0\uC5D0\uC11C\uB3C4 \uC62C\uBC14\uB974\uAC8C \uB3D9\uC791\n *\n * \uACC4\uC0B0\uC5D0 \uD3EC\uD568\uB418\uB294 \uC694\uC18C:\n * - \uBDF0\uD3EC\uD2B8 \uAE30\uC900 \uC704\uCE58 (getBoundingClientRect)\n * - \uBB38\uC11C \uC2A4\uD06C\uB864 \uC704\uCE58 (window.scrollX/Y)\n * - \uBD80\uBAA8 \uC694\uC18C \uB0B4\uBD80 \uC2A4\uD06C\uB864 (parentEl.scrollTop/Left)\n * - \uC911\uAC04 \uC694\uC18C\uB4E4\uC758 border \uB450\uAED8\n * - CSS transform \uBCC0\uD658\n *\n * @param parent - \uAE30\uC900\uC774 \uB420 \uBD80\uBAA8 \uC694\uC18C \uB610\uB294 \uC140\uB809\uD130 (\uC608: document.body, \".container\")\n * @returns CSS top/left \uC18D\uC131\uC5D0 \uC0AC\uC6A9\uD560 \uC218 \uC788\uB294 \uC88C\uD45C\n * @throws {ArgumentError} \uBD80\uBAA8 \uC694\uC18C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uB294 \uACBD\uC6B0\n */\n getRelativeOffset(parent: HTMLElement | string): { top: number; left: number };\n\n /**\n * \uB300\uC0C1\uC774 offset \uC601\uC5ED(\uACE0\uC815 \uD5E4\uB354/\uACE0\uC815 \uC5F4 \uB4F1)\uC5D0 \uAC00\uB824\uC9C4 \uACBD\uC6B0, \uBCF4\uC774\uB3C4\uB85D \uC2A4\uD06C\uB864\n *\n * @remarks\n * \uC774 \uD568\uC218\uB294 \uB300\uC0C1\uC774 \uC2A4\uD06C\uB864 \uC601\uC5ED\uC758 \uC704\uCABD/\uC67C\uCABD \uACBD\uACC4\uB97C \uBC97\uC5B4\uB09C \uACBD\uC6B0\uB9CC \uCC98\uB9AC\uD55C\uB2E4.\n * \uC544\uB798\uCABD/\uC624\uB978\uCABD\uC73C\uB85C \uC2A4\uD06C\uB864\uC774 \uD544\uC694\uD55C \uACBD\uC6B0\uB294 \uBE0C\uB77C\uC6B0\uC800\uC758 \uAE30\uBCF8 \uD3EC\uCEE4\uC2A4 \uC2A4\uD06C\uB864 \uB3D9\uC791\uC5D0 \uC758\uC874\uD55C\uB2E4.\n * \uC8FC\uB85C \uACE0\uC815 \uD5E4\uB354\uB098 \uACE0\uC815 \uC5F4\uC774 \uC788\uB294 \uD14C\uC774\uBE14\uC5D0\uC11C \uD3EC\uCEE4\uC2A4 \uC774\uBCA4\uD2B8\uC640 \uD568\uAED8 \uC0AC\uC6A9\uB41C\uB2E4.\n *\n * @param target - \uB300\uC0C1\uC758 \uCEE8\uD14C\uC774\uB108 \uB0B4 \uC704\uCE58 (offsetTop, offsetLeft)\n * @param offset - \uAC00\uB824\uC9C0\uBA74 \uC548 \uB418\uB294 \uC601\uC5ED \uD06C\uAE30 (\uC608: \uACE0\uC815 \uD5E4\uB354 \uB192\uC774, \uACE0\uC815 \uC5F4 \uB108\uBE44)\n */\n scrollIntoViewIfNeeded(target: { top: number; left: number }, offset?: { top: number; left: number }): void;\n }\n}\n\nHTMLElement.prototype.repaint = function (): void {\n // offsetHeight \uC811\uADFC \uC2DC \uBE0C\uB77C\uC6B0\uC800\uB294 \uB3D9\uAE30\uC801 \uB808\uC774\uC544\uC6C3 \uACC4\uC0B0(forced synchronous layout)\uC744 \uC218\uD589\uD558\uBA70,\n // \uC774\uB85C \uC778\uD574 \uD604\uC7AC \uBC30\uCE58\uB41C \uC2A4\uD0C0\uC77C \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC989\uC2DC \uC801\uC6A9\uB418\uC5B4 \uB9AC\uD398\uC778\uD2B8\uAC00 \uD2B8\uB9AC\uAC70\uB41C\uB2E4.\n void this.offsetHeight;\n};\n\nHTMLElement.prototype.getRelativeOffset = function (parent: HTMLElement | string): { top: number; left: number } {\n const parentEl = typeof parent === \"string\" ? this.closest(parent) : parent;\n\n if (!(parentEl instanceof HTMLElement)) {\n throw new ArgumentError({ parent });\n }\n\n const elementRect = this.getBoundingClientRect();\n const parentRect = parentEl.getBoundingClientRect();\n\n const scrollLeft = window.scrollX;\n const scrollTop = window.scrollY;\n\n const relativeOffset = {\n top: elementRect.top - parentRect.top + scrollTop + (parentEl.scrollTop || 0),\n left: elementRect.left - parentRect.left + scrollLeft + (parentEl.scrollLeft || 0),\n };\n\n let currentEl = this.parentElement;\n while (currentEl !== null && currentEl !== parentEl) {\n const style = getComputedStyle(currentEl);\n relativeOffset.top += parseFloat(style.borderTopWidth) || 0;\n relativeOffset.left += parseFloat(style.borderLeftWidth) || 0;\n currentEl = currentEl.parentElement;\n }\n\n const elTransform = getComputedStyle(this).transform;\n const parentTransform = getComputedStyle(parentEl).transform;\n\n if (elTransform !== \"none\" || parentTransform !== \"none\") {\n const elementMatrix = new DOMMatrix(elTransform);\n const parentMatrix = new DOMMatrix(parentTransform);\n\n if (!elementMatrix.isIdentity || !parentMatrix.isIdentity) {\n const transformedPoint = parentMatrix\n .inverse()\n .multiply(elementMatrix)\n .transformPoint(new DOMPoint(relativeOffset.left, relativeOffset.top));\n\n relativeOffset.left = transformedPoint.x;\n relativeOffset.top = transformedPoint.y;\n }\n }\n\n return relativeOffset;\n};\n\nHTMLElement.prototype.scrollIntoViewIfNeeded = function (\n target: { top: number; left: number },\n offset: { top: number; left: number } = { top: 0, left: 0 },\n): void {\n const scroll = {\n top: this.scrollTop,\n left: this.scrollLeft,\n };\n\n if (target.top - scroll.top < offset.top) {\n this.scrollTop = target.top - offset.top;\n }\n if (target.left - scroll.left < offset.left) {\n this.scrollLeft = target.left - offset.left;\n }\n};\n"],
5
4
  "mappings": "AAAA,SAAS,qBAAqB;AAgD9B,YAAY,UAAU,UAAU,WAAkB;AAGhD,OAAK,KAAK;AACZ;AAEA,YAAY,UAAU,oBAAoB,SAAU,QAA6D;AAC/G,QAAM,WAAW,OAAO,WAAW,WAAW,KAAK,QAAQ,MAAM,IAAI;AAErE,MAAI,EAAE,oBAAoB,cAAc;AACtC,UAAM,IAAI,cAAc,EAAE,OAAO,CAAC;AAAA,EACpC;AAEA,QAAM,cAAc,KAAK,sBAAsB;AAC/C,QAAM,aAAa,SAAS,sBAAsB;AAElD,QAAM,aAAa,OAAO;AAC1B,QAAM,YAAY,OAAO;AAEzB,QAAM,iBAAiB;AAAA,IACrB,KAAK,YAAY,MAAM,WAAW,MAAM,aAAa,SAAS,aAAa;AAAA,IAC3E,MAAM,YAAY,OAAO,WAAW,OAAO,cAAc,SAAS,cAAc;AAAA,EAClF;AAEA,MAAI,YAAY,KAAK;AACrB,SAAO,cAAc,QAAQ,cAAc,UAAU;AACnD,UAAM,QAAQ,iBAAiB,SAAS;AACxC,mBAAe,OAAO,WAAW,MAAM,cAAc,KAAK;AAC1D,mBAAe,QAAQ,WAAW,MAAM,eAAe,KAAK;AAC5D,gBAAY,UAAU;AAAA,EACxB;AAEA,QAAM,cAAc,iBAAiB,IAAI,EAAE;AAC3C,QAAM,kBAAkB,iBAAiB,QAAQ,EAAE;AAEnD,MAAI,gBAAgB,UAAU,oBAAoB,QAAQ;AACxD,UAAM,gBAAgB,IAAI,UAAU,WAAW;AAC/C,UAAM,eAAe,IAAI,UAAU,eAAe;AAElD,QAAI,CAAC,cAAc,cAAc,CAAC,aAAa,YAAY;AACzD,YAAM,mBAAmB,aACtB,QAAQ,EACR,SAAS,aAAa,EACtB,eAAe,IAAI,SAAS,eAAe,MAAM,eAAe,GAAG,CAAC;AAEvE,qBAAe,OAAO,iBAAiB;AACvC,qBAAe,MAAM,iBAAiB;AAAA,IACxC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,YAAY,UAAU,yBAAyB,SAC7C,QACA,SAAwC,EAAE,KAAK,GAAG,MAAM,EAAE,GACpD;AACN,QAAM,SAAS;AAAA,IACb,KAAK,KAAK;AAAA,IACV,MAAM,KAAK;AAAA,EACb;AAEA,MAAI,OAAO,MAAM,OAAO,MAAM,OAAO,KAAK;AACxC,SAAK,YAAY,OAAO,MAAM,OAAO;AAAA,EACvC;AACA,MAAI,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM;AAC3C,SAAK,aAAa,OAAO,OAAO,OAAO;AAAA,EACzC;AACF;",
6
5
  "names": []
7
6
  }
@@ -2,6 +2,7 @@ import "./extensions/element-ext";
2
2
  import "./extensions/html-element-ext";
3
3
  export * from "./extensions/element-ext";
4
4
  export * from "./extensions/html-element-ext";
5
- export * from "./utils/blob";
6
5
  export * from "./utils/download";
6
+ export * from "./utils/fetch";
7
+ export * from "./utils/file-dialog";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,0BAA0B,CAAC;AAClC,OAAO,+BAA+B,CAAC;AAGvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
- import "./extensions/element-ext";
2
- import "./extensions/html-element-ext";
3
- export * from "./extensions/element-ext";
4
- export * from "./extensions/html-element-ext";
5
- export * from "./utils/blob";
6
- export * from "./utils/download";
1
+ import "./extensions/element-ext.js";
2
+ import "./extensions/html-element-ext.js";
3
+ export * from "./extensions/element-ext.js";
4
+ export * from "./extensions/html-element-ext.js";
5
+ export * from "./utils/download.js";
6
+ export * from "./utils/fetch.js";
7
+ export * from "./utils/file-dialog.js";
7
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["// core-browser: \uBE0C\uB77C\uC6B0\uC800 \uC804\uC6A9 \uC720\uD2F8\uB9AC\uD2F0\n\n// extensions (side-effect)\nimport \"./extensions/element-ext\";\nimport \"./extensions/html-element-ext\";\n\n// re-exports\nexport * from \"./extensions/element-ext\";\nexport * from \"./extensions/html-element-ext\";\nexport * from \"./utils/blob\";\nexport * from \"./utils/download\";\n"],
5
- "mappings": "AAGA,OAAO;AACP,OAAO;AAGP,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
4
+ "mappings": "AAGA,OAAO;AACP,OAAO;AAGP,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
6
5
  "names": []
7
6
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Blob을 파일로 다운로드
3
+ *
4
+ * @param blob - 다운로드할 Blob 객체
5
+ * @param fileName - 저장될 파일 이름
6
+ */
7
+ export declare function downloadBlob(blob: Blob, fileName: string): void;
8
+ //# sourceMappingURL=download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/utils/download.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAU/D"}
@@ -1,47 +1,15 @@
1
- async function downloadBytes(url, options) {
2
- var _a, _b;
3
- const response = await fetch(url);
4
- if (!response.ok) {
5
- throw new Error(`Download failed: ${response.status} ${response.statusText}`);
6
- }
7
- const contentLength = Number(response.headers.get("Content-Length") ?? 0);
8
- const reader = (_a = response.body) == null ? void 0 : _a.getReader();
9
- if (!reader) {
10
- throw new Error("Response body is not readable");
11
- }
1
+ function downloadBlob(blob, fileName) {
2
+ const url = URL.createObjectURL(blob);
12
3
  try {
13
- if (contentLength > 0) {
14
- const result2 = new Uint8Array(contentLength);
15
- let receivedLength2 = 0;
16
- while (true) {
17
- const { done, value } = await reader.read();
18
- if (done) break;
19
- result2.set(value, receivedLength2);
20
- receivedLength2 += value.length;
21
- (_b = options == null ? void 0 : options.onProgress) == null ? void 0 : _b.call(options, { receivedLength: receivedLength2, contentLength });
22
- }
23
- return result2;
24
- }
25
- const chunks = [];
26
- let receivedLength = 0;
27
- while (true) {
28
- const { done, value } = await reader.read();
29
- if (done) break;
30
- chunks.push(value);
31
- receivedLength += value.length;
32
- }
33
- const result = new Uint8Array(receivedLength);
34
- let position = 0;
35
- for (const chunk of chunks) {
36
- result.set(chunk, position);
37
- position += chunk.length;
38
- }
39
- return result;
4
+ const link = document.createElement("a");
5
+ link.href = url;
6
+ link.download = fileName;
7
+ link.click();
40
8
  } finally {
41
- reader.releaseLock();
9
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
42
10
  }
43
11
  }
44
12
  export {
45
- downloadBytes
13
+ downloadBlob
46
14
  };
47
15
  //# sourceMappingURL=download.js.map
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/download.ts"],
4
- "sourcesContent": ["export interface DownloadProgress {\n receivedLength: number;\n contentLength: number;\n}\n\n/**\n * URL\uC5D0\uC11C \uBC14\uC774\uB108\uB9AC \uB370\uC774\uD130 \uB2E4\uC6B4\uB85C\uB4DC (\uC9C4\uD589\uB960 \uCF5C\uBC31 \uC9C0\uC6D0)\n */\nexport async function downloadBytes(\n url: string,\n options?: { onProgress?: (progress: DownloadProgress) => void },\n): Promise<Uint8Array> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Download failed: ${response.status} ${response.statusText}`);\n }\n\n const contentLength = Number(response.headers.get(\"Content-Length\") ?? 0);\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Response body is not readable\");\n }\n\n try {\n // Content-Length\uB97C \uC54C \uC218 \uC788\uC73C\uBA74 \uBBF8\uB9AC \uD560\uB2F9\uD558\uC5EC \uBA54\uBAA8\uB9AC \uD6A8\uC728\uC131 \uD5A5\uC0C1\n if (contentLength > 0) {\n const result = new Uint8Array(contentLength);\n let receivedLength = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n result.set(value, receivedLength);\n receivedLength += value.length;\n options?.onProgress?.({ receivedLength, contentLength });\n }\n\n return result;\n }\n\n // Content-Length\uB97C \uBAA8\uB974\uBA74 \uCCAD\uD06C \uC218\uC9D1 \uD6C4 \uBCD1\uD569 (chunked encoding)\n const chunks: Uint8Array[] = [];\n let receivedLength = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n chunks.push(value);\n receivedLength += value.length;\n }\n\n // \uCCAD\uD06C \uBCD1\uD569\n const result = new Uint8Array(receivedLength);\n let position = 0;\n for (const chunk of chunks) {\n result.set(chunk, position);\n position += chunk.length;\n }\n\n return result;\n } finally {\n reader.releaseLock();\n }\n}\n"],
5
- "mappings": "AAQA,eAAsB,cACpB,KACA,SACqB;AAXvB;AAYE,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,oBAAoB,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,EAC9E;AAEA,QAAM,gBAAgB,OAAO,SAAS,QAAQ,IAAI,gBAAgB,KAAK,CAAC;AACxE,QAAM,UAAS,cAAS,SAAT,mBAAe;AAC9B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,MAAI;AAEF,QAAI,gBAAgB,GAAG;AACrB,YAAMA,UAAS,IAAI,WAAW,aAAa;AAC3C,UAAIC,kBAAiB;AAErB,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,QAAAD,QAAO,IAAI,OAAOC,eAAc;AAChC,QAAAA,mBAAkB,MAAM;AACxB,iDAAS,eAAT,iCAAsB,EAAE,gBAAAA,iBAAgB,cAAc;AAAA,MACxD;AAEA,aAAOD;AAAA,IACT;AAGA,UAAM,SAAuB,CAAC;AAC9B,QAAI,iBAAiB;AAErB,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AAEV,aAAO,KAAK,KAAK;AACjB,wBAAkB,MAAM;AAAA,IAC1B;AAGA,UAAM,SAAS,IAAI,WAAW,cAAc;AAC5C,QAAI,WAAW;AACf,eAAW,SAAS,QAAQ;AAC1B,aAAO,IAAI,OAAO,QAAQ;AAC1B,kBAAY,MAAM;AAAA,IACpB;AAEA,WAAO;AAAA,EACT,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;",
6
- "names": ["result", "receivedLength"]
4
+ "mappings": "AAMO,SAAS,aAAa,MAAY,UAAwB;AAC/D,QAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,MAAI;AACF,UAAM,OAAO,SAAS,cAAc,GAAG;AACvC,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,MAAM;AAAA,EACb,UAAE;AACA,eAAW,MAAM,IAAI,gBAAgB,GAAG,GAAG,GAAI;AAAA,EACjD;AACF;",
5
+ "names": []
7
6
  }
@@ -5,7 +5,7 @@ export interface DownloadProgress {
5
5
  /**
6
6
  * URL에서 바이너리 데이터 다운로드 (진행률 콜백 지원)
7
7
  */
8
- export declare function downloadBytes(url: string, options?: {
8
+ export declare function fetchUrlBytes(url: string, options?: {
9
9
  onProgress?: (progress: DownloadProgress) => void;
10
10
  }): Promise<Uint8Array>;
11
- //# sourceMappingURL=download.d.ts.map
11
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAA;CAAE,GAC9D,OAAO,CAAC,UAAU,CAAC,CAsDrB"}
@@ -0,0 +1,47 @@
1
+ async function fetchUrlBytes(url, options) {
2
+ var _a, _b;
3
+ const response = await fetch(url);
4
+ if (!response.ok) {
5
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
6
+ }
7
+ const contentLength = Number(response.headers.get("Content-Length") ?? 0);
8
+ const reader = (_a = response.body) == null ? void 0 : _a.getReader();
9
+ if (!reader) {
10
+ throw new Error("Response body is not readable");
11
+ }
12
+ try {
13
+ if (contentLength > 0) {
14
+ const result2 = new Uint8Array(contentLength);
15
+ let receivedLength2 = 0;
16
+ while (true) {
17
+ const { done, value } = await reader.read();
18
+ if (done) break;
19
+ result2.set(value, receivedLength2);
20
+ receivedLength2 += value.length;
21
+ (_b = options == null ? void 0 : options.onProgress) == null ? void 0 : _b.call(options, { receivedLength: receivedLength2, contentLength });
22
+ }
23
+ return result2;
24
+ }
25
+ const chunks = [];
26
+ let receivedLength = 0;
27
+ while (true) {
28
+ const { done, value } = await reader.read();
29
+ if (done) break;
30
+ chunks.push(value);
31
+ receivedLength += value.length;
32
+ }
33
+ const result = new Uint8Array(receivedLength);
34
+ let position = 0;
35
+ for (const chunk of chunks) {
36
+ result.set(chunk, position);
37
+ position += chunk.length;
38
+ }
39
+ return result;
40
+ } finally {
41
+ reader.releaseLock();
42
+ }
43
+ }
44
+ export {
45
+ fetchUrlBytes
46
+ };
47
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/fetch.ts"],
4
+ "mappings": "AAQA,eAAsB,cACpB,KACA,SACqB;AAXvB;AAYE,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,oBAAoB,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,EAC9E;AAEA,QAAM,gBAAgB,OAAO,SAAS,QAAQ,IAAI,gBAAgB,KAAK,CAAC;AACxE,QAAM,UAAS,cAAS,SAAT,mBAAe;AAC9B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,MAAI;AAEF,QAAI,gBAAgB,GAAG;AACrB,YAAMA,UAAS,IAAI,WAAW,aAAa;AAC3C,UAAIC,kBAAiB;AAErB,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,QAAAD,QAAO,IAAI,OAAOC,eAAc;AAChC,QAAAA,mBAAkB,MAAM;AACxB,iDAAS,eAAT,iCAAsB,EAAE,gBAAAA,iBAAgB,cAAc;AAAA,MACxD;AAEA,aAAOD;AAAA,IACT;AAGA,UAAM,SAAuB,CAAC;AAC9B,QAAI,iBAAiB;AAErB,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AAEV,aAAO,KAAK,KAAK;AACjB,wBAAkB,MAAM;AAAA,IAC1B;AAGA,UAAM,SAAS,IAAI,WAAW,cAAc;AAC5C,QAAI,WAAW;AACf,eAAW,SAAS,QAAQ;AAC1B,aAAO,IAAI,OAAO,QAAQ;AAC1B,kBAAY,MAAM;AAAA,IACpB;AAEA,WAAO;AAAA,EACT,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;",
5
+ "names": ["result", "receivedLength"]
6
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 파일 선택 다이얼로그를 프로그래밍 방식으로 열기
3
+ */
4
+ export declare function openFileDialog(options?: {
5
+ accept?: string;
6
+ multiple?: boolean;
7
+ }): Promise<File[] | undefined>;
8
+ //# sourceMappingURL=file-dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-dialog.d.ts","sourceRoot":"","sources":["../../src/utils/file-dialog.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,SAAS,CAAC,CAa7G"}
@@ -0,0 +1,18 @@
1
+ function openFileDialog(options) {
2
+ return new Promise((resolve) => {
3
+ const input = document.createElement("input");
4
+ input.type = "file";
5
+ input.multiple = (options == null ? void 0 : options.multiple) ?? false;
6
+ if ((options == null ? void 0 : options.accept) != null) {
7
+ input.accept = options.accept;
8
+ }
9
+ input.onchange = () => {
10
+ resolve(input.files != null && input.files.length > 0 ? [...input.files] : void 0);
11
+ };
12
+ input.click();
13
+ });
14
+ }
15
+ export {
16
+ openFileDialog
17
+ };
18
+ //# sourceMappingURL=file-dialog.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/file-dialog.ts"],
4
+ "mappings": "AAGO,SAAS,eAAe,SAAgF;AAC7G,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,OAAO;AACb,UAAM,YAAW,mCAAS,aAAY;AACtC,SAAI,mCAAS,WAAU,MAAM;AAC3B,YAAM,SAAS,QAAQ;AAAA,IACzB;AACA,UAAM,WAAW,MAAM;AACrB,cAAQ,MAAM,SAAS,QAAQ,MAAM,MAAM,SAAS,IAAI,CAAC,GAAG,MAAM,KAAK,IAAI,MAAS;AAAA,IACtF;AACA,UAAM,MAAM;AAAA,EACd,CAAC;AACH;",
5
+ "names": []
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/core-browser",
3
- "version": "13.0.0-beta.6",
3
+ "version": "13.0.1",
4
4
  "description": "심플리즘 패키지 - 코어 모듈 (browser)",
5
5
  "author": "김석래",
6
6
  "repository": {
@@ -12,15 +12,19 @@
12
12
  "type": "module",
13
13
  "main": "./dist/index.js",
14
14
  "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
15
19
  "sideEffects": [
16
20
  "./dist/extensions/element-ext.js",
17
21
  "./dist/extensions/html-element-ext.js"
18
22
  ],
19
23
  "dependencies": {
20
- "@simplysm/core-common": "workspace:*",
21
- "tabbable": "^6.4.0"
24
+ "tabbable": "^6.4.0",
25
+ "@simplysm/core-common": "13.0.1"
22
26
  },
23
27
  "devDependencies": {
24
- "happy-dom": "^20.3.4"
28
+ "happy-dom": "^20.6.1"
25
29
  }
26
- }
30
+ }
package/src/index.ts CHANGED
@@ -7,5 +7,6 @@ import "./extensions/html-element-ext";
7
7
  // re-exports
8
8
  export * from "./extensions/element-ext";
9
9
  export * from "./extensions/html-element-ext";
10
- export * from "./utils/blob";
11
10
  export * from "./utils/download";
11
+ export * from "./utils/fetch";
12
+ export * from "./utils/file-dialog";
@@ -1,66 +1,17 @@
1
- export interface DownloadProgress {
2
- receivedLength: number;
3
- contentLength: number;
4
- }
5
-
6
1
  /**
7
- * URL에서 바이너리 데이터 다운로드 (진행률 콜백 지원)
2
+ * Blob을 파일로 다운로드
3
+ *
4
+ * @param blob - 다운로드할 Blob 객체
5
+ * @param fileName - 저장될 파일 이름
8
6
  */
9
- export async function downloadBytes(
10
- url: string,
11
- options?: { onProgress?: (progress: DownloadProgress) => void },
12
- ): Promise<Uint8Array> {
13
- const response = await fetch(url);
14
- if (!response.ok) {
15
- throw new Error(`Download failed: ${response.status} ${response.statusText}`);
16
- }
17
-
18
- const contentLength = Number(response.headers.get("Content-Length") ?? 0);
19
- const reader = response.body?.getReader();
20
- if (!reader) {
21
- throw new Error("Response body is not readable");
22
- }
23
-
7
+ export function downloadBlob(blob: Blob, fileName: string): void {
8
+ const url = URL.createObjectURL(blob);
24
9
  try {
25
- // Content-Length를 수 있으면 미리 할당하여 메모리 효율성 향상
26
- if (contentLength > 0) {
27
- const result = new Uint8Array(contentLength);
28
- let receivedLength = 0;
29
-
30
- while (true) {
31
- const { done, value } = await reader.read();
32
- if (done) break;
33
-
34
- result.set(value, receivedLength);
35
- receivedLength += value.length;
36
- options?.onProgress?.({ receivedLength, contentLength });
37
- }
38
-
39
- return result;
40
- }
41
-
42
- // Content-Length를 모르면 청크 수집 후 병합 (chunked encoding)
43
- const chunks: Uint8Array[] = [];
44
- let receivedLength = 0;
45
-
46
- while (true) {
47
- const { done, value } = await reader.read();
48
- if (done) break;
49
-
50
- chunks.push(value);
51
- receivedLength += value.length;
52
- }
53
-
54
- // 청크 병합
55
- const result = new Uint8Array(receivedLength);
56
- let position = 0;
57
- for (const chunk of chunks) {
58
- result.set(chunk, position);
59
- position += chunk.length;
60
- }
61
-
62
- return result;
10
+ const link = document.createElement("a");
11
+ link.href = url;
12
+ link.download = fileName;
13
+ link.click();
63
14
  } finally {
64
- reader.releaseLock();
15
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
65
16
  }
66
17
  }
@@ -0,0 +1,66 @@
1
+ export interface DownloadProgress {
2
+ receivedLength: number;
3
+ contentLength: number;
4
+ }
5
+
6
+ /**
7
+ * URL에서 바이너리 데이터 다운로드 (진행률 콜백 지원)
8
+ */
9
+ export async function fetchUrlBytes(
10
+ url: string,
11
+ options?: { onProgress?: (progress: DownloadProgress) => void },
12
+ ): Promise<Uint8Array> {
13
+ const response = await fetch(url);
14
+ if (!response.ok) {
15
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
16
+ }
17
+
18
+ const contentLength = Number(response.headers.get("Content-Length") ?? 0);
19
+ const reader = response.body?.getReader();
20
+ if (!reader) {
21
+ throw new Error("Response body is not readable");
22
+ }
23
+
24
+ try {
25
+ // Content-Length를 알 수 있으면 미리 할당하여 메모리 효율성 향상
26
+ if (contentLength > 0) {
27
+ const result = new Uint8Array(contentLength);
28
+ let receivedLength = 0;
29
+
30
+ while (true) {
31
+ const { done, value } = await reader.read();
32
+ if (done) break;
33
+
34
+ result.set(value, receivedLength);
35
+ receivedLength += value.length;
36
+ options?.onProgress?.({ receivedLength, contentLength });
37
+ }
38
+
39
+ return result;
40
+ }
41
+
42
+ // Content-Length를 모르면 청크 수집 후 병합 (chunked encoding)
43
+ const chunks: Uint8Array[] = [];
44
+ let receivedLength = 0;
45
+
46
+ while (true) {
47
+ const { done, value } = await reader.read();
48
+ if (done) break;
49
+
50
+ chunks.push(value);
51
+ receivedLength += value.length;
52
+ }
53
+
54
+ // 청크 병합
55
+ const result = new Uint8Array(receivedLength);
56
+ let position = 0;
57
+ for (const chunk of chunks) {
58
+ result.set(chunk, position);
59
+ position += chunk.length;
60
+ }
61
+
62
+ return result;
63
+ } finally {
64
+ reader.releaseLock();
65
+ }
66
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 파일 선택 다이얼로그를 프로그래밍 방식으로 열기
3
+ */
4
+ export function openFileDialog(options?: { accept?: string; multiple?: boolean }): Promise<File[] | undefined> {
5
+ return new Promise((resolve) => {
6
+ const input = document.createElement("input");
7
+ input.type = "file";
8
+ input.multiple = options?.multiple ?? false;
9
+ if (options?.accept != null) {
10
+ input.accept = options.accept;
11
+ }
12
+ input.onchange = () => {
13
+ resolve(input.files != null && input.files.length > 0 ? [...input.files] : undefined);
14
+ };
15
+ input.click();
16
+ });
17
+ }