28 changed files with 739 additions and 143 deletions
@ -0,0 +1,18 @@ |
|||||
|
using Microsoft.JSInterop; |
||||
|
|
||||
|
namespace Avalonia.Web.Blazor.Interop |
||||
|
{ |
||||
|
internal class AvaloniaModule : JSModuleInterop |
||||
|
{ |
||||
|
private AvaloniaModule(IJSRuntime js) : base(js, "./_content/Avalonia.Web.Blazor/avalonia.js") |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public static async Task<AvaloniaModule> ImportAsync(IJSRuntime js) |
||||
|
{ |
||||
|
var interop = new AvaloniaModule(js); |
||||
|
await interop.ImportAsync(); |
||||
|
return interop; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,41 +1,32 @@ |
|||||
using Microsoft.AspNetCore.Components; |
using Microsoft.AspNetCore.Components; |
||||
using Microsoft.JSInterop; |
|
||||
using SkiaSharp; |
|
||||
|
|
||||
namespace Avalonia.Web.Blazor.Interop |
namespace Avalonia.Web.Blazor.Interop |
||||
{ |
{ |
||||
internal class InputHelperInterop : JSModuleInterop |
internal class InputHelperInterop |
||||
{ |
{ |
||||
private const string JsFilename = "./_content/Avalonia.Web.Blazor/InputHelper.js"; |
|
||||
private const string ClearSymbol = "InputHelper.clear"; |
private const string ClearSymbol = "InputHelper.clear"; |
||||
private const string FocusSymbol = "InputHelper.focus"; |
private const string FocusSymbol = "InputHelper.focus"; |
||||
private const string SetCursorSymbol = "InputHelper.setCursor"; |
private const string SetCursorSymbol = "InputHelper.setCursor"; |
||||
private const string HideSymbol = "InputHelper.hide"; |
private const string HideSymbol = "InputHelper.hide"; |
||||
private const string ShowSymbol = "InputHelper.show"; |
private const string ShowSymbol = "InputHelper.show"; |
||||
|
|
||||
private readonly ElementReference inputElement; |
private readonly AvaloniaModule _module; |
||||
|
private readonly ElementReference _inputElement; |
||||
|
|
||||
public static async Task<InputHelperInterop> ImportAsync(IJSRuntime js, ElementReference element) |
public InputHelperInterop(AvaloniaModule module, ElementReference element) |
||||
{ |
{ |
||||
var interop = new InputHelperInterop(js, element); |
_module = module; |
||||
await interop.ImportAsync(); |
_inputElement = element; |
||||
return interop; |
|
||||
} |
} |
||||
|
|
||||
public InputHelperInterop(IJSRuntime js, ElementReference element) |
public void Clear() => _module.Invoke(ClearSymbol, _inputElement); |
||||
: base(js, JsFilename) |
|
||||
{ |
|
||||
inputElement = element; |
|
||||
} |
|
||||
|
|
||||
public void Clear() => Invoke(ClearSymbol, inputElement); |
|
||||
|
|
||||
public void Focus() => Invoke(FocusSymbol, inputElement); |
public void Focus() => _module.Invoke(FocusSymbol, _inputElement); |
||||
|
|
||||
public void SetCursor(string kind) => Invoke(SetCursorSymbol, inputElement, kind); |
public void SetCursor(string kind) => _module.Invoke(SetCursorSymbol, _inputElement, kind); |
||||
|
|
||||
public void Hide() => Invoke(HideSymbol, inputElement); |
public void Hide() => _module.Invoke(HideSymbol, _inputElement); |
||||
|
|
||||
public void Show() => Invoke(ShowSymbol, inputElement); |
public void Show() => _module.Invoke(ShowSymbol, _inputElement); |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,5 @@ |
|||||
|
export { DpiWatcher } from "./DpiWatcher" |
||||
|
export { InputHelper } from "./InputHelper" |
||||
|
export { NativeControlHost } from "./NativeControlHost" |
||||
|
export { SizeWatcher } from "./SizeWatcher" |
||||
|
export { SKHtmlCanvas } from "./SKHtmlCanvas" |
||||
@ -1 +0,0 @@ |
|||||
|
|
||||
@ -0,0 +1,40 @@ |
|||||
|
const path = require('path'); |
||||
|
const prod = process.env.NODE_ENV == 'production'; |
||||
|
|
||||
|
module.exports = { |
||||
|
mode: prod ? "production" : "development", |
||||
|
devtool: 'source-map', |
||||
|
target: ["web", "es2020"], |
||||
|
entry: { |
||||
|
avalonia: './modules/Avalonia/Avalonia.ts', |
||||
|
avaloniaStorage: { |
||||
|
import: './modules/Storage/StorageProvider.ts', |
||||
|
dependOn: 'avalonia', |
||||
|
} |
||||
|
}, |
||||
|
output: { |
||||
|
filename: '[name].js', |
||||
|
path: path.resolve(__dirname, '../wwwroot'), |
||||
|
library: { |
||||
|
type: 'module', |
||||
|
}, |
||||
|
}, |
||||
|
module: { |
||||
|
rules: [ |
||||
|
{ |
||||
|
test: /\.tsx?$/, |
||||
|
use: 'ts-loader', |
||||
|
exclude: /node_modules/, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
resolve: { |
||||
|
extensions: ['.ts', '.js'], |
||||
|
}, |
||||
|
optimization: { |
||||
|
minimize: false |
||||
|
}, |
||||
|
experiments: { |
||||
|
outputModule: true, |
||||
|
} |
||||
|
}; |
||||
@ -0,0 +1,6 @@ |
|||||
|
export { DpiWatcher } from "./DpiWatcher"; |
||||
|
export { InputHelper } from "./InputHelper"; |
||||
|
export { NativeControlHost } from "./NativeControlHost"; |
||||
|
export { SizeWatcher } from "./SizeWatcher"; |
||||
|
export { SKHtmlCanvas } from "./SKHtmlCanvas"; |
||||
|
//# sourceMappingURL=Avalonia.js.map
|
||||
@ -0,0 +1,26 @@ |
|||||
|
export class DpiWatcher { |
||||
|
static getDpi() { |
||||
|
return window.devicePixelRatio; |
||||
|
} |
||||
|
static start(callback) { |
||||
|
DpiWatcher.lastDpi = window.devicePixelRatio; |
||||
|
DpiWatcher.timerId = window.setInterval(DpiWatcher.update, 1000); |
||||
|
DpiWatcher.callback = callback; |
||||
|
return DpiWatcher.lastDpi; |
||||
|
} |
||||
|
static stop() { |
||||
|
window.clearInterval(DpiWatcher.timerId); |
||||
|
DpiWatcher.callback = undefined; |
||||
|
} |
||||
|
static update() { |
||||
|
if (!DpiWatcher.callback) |
||||
|
return; |
||||
|
const currentDpi = window.devicePixelRatio; |
||||
|
const lastDpi = DpiWatcher.lastDpi; |
||||
|
DpiWatcher.lastDpi = currentDpi; |
||||
|
if (Math.abs(lastDpi - currentDpi) > 0.001) { |
||||
|
DpiWatcher.callback.invokeMethod('Invoke', lastDpi, currentDpi); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//# sourceMappingURL=DpiWatcher.js.map
|
||||
@ -0,0 +1,19 @@ |
|||||
|
export class InputHelper { |
||||
|
static clear(inputElement) { |
||||
|
inputElement.value = ""; |
||||
|
} |
||||
|
static focus(inputElement) { |
||||
|
inputElement.focus(); |
||||
|
inputElement.setSelectionRange(0, 0); |
||||
|
} |
||||
|
static setCursor(inputElement, kind) { |
||||
|
inputElement.style.cursor = kind; |
||||
|
} |
||||
|
static hide(inputElement) { |
||||
|
inputElement.style.display = 'none'; |
||||
|
} |
||||
|
static show(inputElement) { |
||||
|
inputElement.style.display = 'block'; |
||||
|
} |
||||
|
} |
||||
|
//# sourceMappingURL=InputHelper.js.map
|
||||
@ -0,0 +1,48 @@ |
|||||
|
export class NativeControlHost { |
||||
|
static CreateDefaultChild(parent) { |
||||
|
return document.createElement("div"); |
||||
|
} |
||||
|
static GetReference(element) { |
||||
|
return element; |
||||
|
} |
||||
|
static CreateAttachment() { |
||||
|
return new NativeControlHostTopLevelAttachment(); |
||||
|
} |
||||
|
} |
||||
|
class NativeControlHostTopLevelAttachment { |
||||
|
InitializeWithChildHandle(child) { |
||||
|
this._child = child; |
||||
|
this._child.style.position = "absolute"; |
||||
|
} |
||||
|
AttachTo(host) { |
||||
|
if (this._host && this._child) { |
||||
|
this._host.removeChild(this._child); |
||||
|
} |
||||
|
this._host = host; |
||||
|
if (this._host && this._child) { |
||||
|
this._host.appendChild(this._child); |
||||
|
} |
||||
|
} |
||||
|
ShowInBounds(x, y, width, height) { |
||||
|
if (this._child) { |
||||
|
this._child.style.top = y + "px"; |
||||
|
this._child.style.left = x + "px"; |
||||
|
this._child.style.width = width + "px"; |
||||
|
this._child.style.height = height + "px"; |
||||
|
this._child.style.display = "block"; |
||||
|
} |
||||
|
} |
||||
|
HideWithSize(width, height) { |
||||
|
if (this._child) { |
||||
|
this._child.style.width = width + "px"; |
||||
|
this._child.style.height = height + "px"; |
||||
|
this._child.style.display = "none"; |
||||
|
} |
||||
|
} |
||||
|
ReleaseChild() { |
||||
|
if (this._child) { |
||||
|
this._child = undefined; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//# sourceMappingURL=NativeControlHost.js.map
|
||||
@ -0,0 +1,172 @@ |
|||||
|
export class SKHtmlCanvas { |
||||
|
constructor(useGL, element, callback) { |
||||
|
this.renderLoopEnabled = false; |
||||
|
this.renderLoopRequest = 0; |
||||
|
this.htmlCanvas = element; |
||||
|
this.renderFrameCallback = callback; |
||||
|
if (useGL) { |
||||
|
const ctx = SKHtmlCanvas.createWebGLContext(this.htmlCanvas); |
||||
|
if (!ctx) { |
||||
|
console.error(`Failed to create WebGL context: err ${ctx}`); |
||||
|
return; |
||||
|
} |
||||
|
GL.makeContextCurrent(ctx); |
||||
|
const fbo = GLctx.getParameter(GLctx.FRAMEBUFFER_BINDING); |
||||
|
this.glInfo = { |
||||
|
context: ctx, |
||||
|
fboId: fbo ? fbo.id : 0, |
||||
|
stencil: GLctx.getParameter(GLctx.STENCIL_BITS), |
||||
|
sample: 0, |
||||
|
depth: GLctx.getParameter(GLctx.DEPTH_BITS), |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
static initGL(element, elementId, callback) { |
||||
|
var view = SKHtmlCanvas.init(true, element, elementId, callback); |
||||
|
if (!view || !view.glInfo) |
||||
|
return null; |
||||
|
return view.glInfo; |
||||
|
} |
||||
|
static initRaster(element, elementId, callback) { |
||||
|
var view = SKHtmlCanvas.init(false, element, elementId, callback); |
||||
|
if (!view) |
||||
|
return false; |
||||
|
return true; |
||||
|
} |
||||
|
static init(useGL, element, elementId, callback) { |
||||
|
var htmlCanvas = element; |
||||
|
if (!htmlCanvas) { |
||||
|
console.error(`No canvas element was provided.`); |
||||
|
return null; |
||||
|
} |
||||
|
if (!SKHtmlCanvas.elements) |
||||
|
SKHtmlCanvas.elements = new Map(); |
||||
|
SKHtmlCanvas.elements.set(elementId, element); |
||||
|
const view = new SKHtmlCanvas(useGL, element, callback); |
||||
|
htmlCanvas.SKHtmlCanvas = view; |
||||
|
return view; |
||||
|
} |
||||
|
static deinit(elementId) { |
||||
|
if (!elementId) |
||||
|
return; |
||||
|
const element = SKHtmlCanvas.elements.get(elementId); |
||||
|
SKHtmlCanvas.elements.delete(elementId); |
||||
|
const htmlCanvas = element; |
||||
|
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) |
||||
|
return; |
||||
|
htmlCanvas.SKHtmlCanvas.deinit(); |
||||
|
htmlCanvas.SKHtmlCanvas = undefined; |
||||
|
} |
||||
|
static requestAnimationFrame(element, renderLoop) { |
||||
|
const htmlCanvas = element; |
||||
|
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) |
||||
|
return; |
||||
|
htmlCanvas.SKHtmlCanvas.requestAnimationFrame(renderLoop); |
||||
|
} |
||||
|
static setCanvasSize(element, width, height) { |
||||
|
const htmlCanvas = element; |
||||
|
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) |
||||
|
return; |
||||
|
htmlCanvas.SKHtmlCanvas.setCanvasSize(width, height); |
||||
|
} |
||||
|
static setEnableRenderLoop(element, enable) { |
||||
|
const htmlCanvas = element; |
||||
|
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) |
||||
|
return; |
||||
|
htmlCanvas.SKHtmlCanvas.setEnableRenderLoop(enable); |
||||
|
} |
||||
|
static putImageData(element, pData, width, height) { |
||||
|
const htmlCanvas = element; |
||||
|
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) |
||||
|
return; |
||||
|
htmlCanvas.SKHtmlCanvas.putImageData(pData, width, height); |
||||
|
} |
||||
|
deinit() { |
||||
|
this.setEnableRenderLoop(false); |
||||
|
} |
||||
|
setCanvasSize(width, height) { |
||||
|
this.newWidth = width; |
||||
|
this.newHeight = height; |
||||
|
if (this.htmlCanvas.width != this.newWidth) { |
||||
|
this.htmlCanvas.width = this.newWidth; |
||||
|
} |
||||
|
if (this.htmlCanvas.height != this.newHeight) { |
||||
|
this.htmlCanvas.height = this.newHeight; |
||||
|
} |
||||
|
if (this.glInfo) { |
||||
|
GL.makeContextCurrent(this.glInfo.context); |
||||
|
} |
||||
|
} |
||||
|
requestAnimationFrame(renderLoop) { |
||||
|
if (renderLoop !== undefined && this.renderLoopEnabled !== renderLoop) |
||||
|
this.setEnableRenderLoop(renderLoop); |
||||
|
if (this.renderLoopRequest !== 0) |
||||
|
return; |
||||
|
this.renderLoopRequest = window.requestAnimationFrame(() => { |
||||
|
if (this.glInfo) { |
||||
|
GL.makeContextCurrent(this.glInfo.context); |
||||
|
} |
||||
|
if (this.htmlCanvas.width != this.newWidth) { |
||||
|
this.htmlCanvas.width = this.newWidth || 0; |
||||
|
} |
||||
|
if (this.htmlCanvas.height != this.newHeight) { |
||||
|
this.htmlCanvas.height = this.newHeight || 0; |
||||
|
} |
||||
|
this.renderFrameCallback.invokeMethod('Invoke'); |
||||
|
this.renderLoopRequest = 0; |
||||
|
if (this.renderLoopEnabled) |
||||
|
this.requestAnimationFrame(); |
||||
|
}); |
||||
|
} |
||||
|
setEnableRenderLoop(enable) { |
||||
|
this.renderLoopEnabled = enable; |
||||
|
if (enable) { |
||||
|
this.requestAnimationFrame(); |
||||
|
} |
||||
|
else if (this.renderLoopRequest !== 0) { |
||||
|
window.cancelAnimationFrame(this.renderLoopRequest); |
||||
|
this.renderLoopRequest = 0; |
||||
|
} |
||||
|
} |
||||
|
putImageData(pData, width, height) { |
||||
|
if (this.glInfo || !pData || width <= 0 || width <= 0) |
||||
|
return false; |
||||
|
var ctx = this.htmlCanvas.getContext('2d'); |
||||
|
if (!ctx) { |
||||
|
console.error(`Failed to obtain 2D canvas context.`); |
||||
|
return false; |
||||
|
} |
||||
|
this.htmlCanvas.width = width; |
||||
|
this.htmlCanvas.height = height; |
||||
|
var buffer = new Uint8ClampedArray(Module.HEAPU8.buffer, pData, width * height * 4); |
||||
|
var imageData = new ImageData(buffer, width, height); |
||||
|
ctx.putImageData(imageData, 0, 0); |
||||
|
return true; |
||||
|
} |
||||
|
static createWebGLContext(htmlCanvas) { |
||||
|
const contextAttributes = { |
||||
|
alpha: 1, |
||||
|
depth: 1, |
||||
|
stencil: 8, |
||||
|
antialias: 0, |
||||
|
premultipliedAlpha: 1, |
||||
|
preserveDrawingBuffer: 0, |
||||
|
preferLowPowerToHighPerformance: 0, |
||||
|
failIfMajorPerformanceCaveat: 0, |
||||
|
majorVersion: 2, |
||||
|
minorVersion: 0, |
||||
|
enableExtensionsByDefault: 1, |
||||
|
explicitSwapControl: 0, |
||||
|
renderViaOffscreenBackBuffer: 1, |
||||
|
}; |
||||
|
let ctx = GL.createContext(htmlCanvas, contextAttributes); |
||||
|
if (!ctx && contextAttributes.majorVersion > 1) { |
||||
|
console.warn('Falling back to WebGL 1.0'); |
||||
|
contextAttributes.majorVersion = 1; |
||||
|
contextAttributes.minorVersion = 0; |
||||
|
ctx = GL.createContext(htmlCanvas, contextAttributes); |
||||
|
} |
||||
|
return ctx; |
||||
|
} |
||||
|
} |
||||
|
//# sourceMappingURL=SKHtmlCanvas.js.map
|
||||
@ -0,0 +1,39 @@ |
|||||
|
export class SizeWatcher { |
||||
|
static observe(element, elementId, callback) { |
||||
|
if (!element || !callback) |
||||
|
return; |
||||
|
SizeWatcher.init(); |
||||
|
const watcherElement = element; |
||||
|
watcherElement.SizeWatcher = { |
||||
|
callback: callback |
||||
|
}; |
||||
|
SizeWatcher.elements.set(elementId, element); |
||||
|
SizeWatcher.observer.observe(element); |
||||
|
SizeWatcher.invoke(element); |
||||
|
} |
||||
|
static unobserve(elementId) { |
||||
|
if (!elementId || !SizeWatcher.observer) |
||||
|
return; |
||||
|
const element = SizeWatcher.elements.get(elementId); |
||||
|
SizeWatcher.elements.delete(elementId); |
||||
|
SizeWatcher.observer.unobserve(element); |
||||
|
} |
||||
|
static init() { |
||||
|
if (SizeWatcher.observer) |
||||
|
return; |
||||
|
SizeWatcher.elements = new Map(); |
||||
|
SizeWatcher.observer = new ResizeObserver((entries) => { |
||||
|
for (let entry of entries) { |
||||
|
SizeWatcher.invoke(entry.target); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
static invoke(element) { |
||||
|
const watcherElement = element; |
||||
|
const instance = watcherElement.SizeWatcher; |
||||
|
if (!instance || !instance.callback) |
||||
|
return; |
||||
|
return instance.callback.invokeMethod('Invoke', element.clientWidth, element.clientHeight); |
||||
|
} |
||||
|
} |
||||
|
//# sourceMappingURL=SizeWatcher.js.map
|
||||
@ -0,0 +1,72 @@ |
|||||
|
class InnerDbConnection { |
||||
|
constructor(database) { |
||||
|
this.database = database; |
||||
|
} |
||||
|
openStore(store, mode) { |
||||
|
const tx = this.database.transaction(store, mode); |
||||
|
return tx.objectStore(store); |
||||
|
} |
||||
|
put(store, obj, key) { |
||||
|
const os = this.openStore(store, "readwrite"); |
||||
|
return new Promise((resolve, reject) => { |
||||
|
const response = os.put(obj, key); |
||||
|
response.onsuccess = () => { |
||||
|
resolve(response.result); |
||||
|
}; |
||||
|
response.onerror = () => { |
||||
|
reject(response.error); |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
get(store, key) { |
||||
|
const os = this.openStore(store, "readonly"); |
||||
|
return new Promise((resolve, reject) => { |
||||
|
const response = os.get(key); |
||||
|
response.onsuccess = () => { |
||||
|
resolve(response.result); |
||||
|
}; |
||||
|
response.onerror = () => { |
||||
|
reject(response.error); |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
delete(store, key) { |
||||
|
const os = this.openStore(store, "readwrite"); |
||||
|
return new Promise((resolve, reject) => { |
||||
|
const response = os.delete(key); |
||||
|
response.onsuccess = () => { |
||||
|
resolve(); |
||||
|
}; |
||||
|
response.onerror = () => { |
||||
|
reject(response.error); |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
close() { |
||||
|
this.database.close(); |
||||
|
} |
||||
|
} |
||||
|
export class IndexedDbWrapper { |
||||
|
constructor(databaseName, objectStores) { |
||||
|
this.databaseName = databaseName; |
||||
|
this.objectStores = objectStores; |
||||
|
} |
||||
|
connect() { |
||||
|
const conn = window.indexedDB.open(this.databaseName, 1); |
||||
|
conn.onupgradeneeded = event => { |
||||
|
const db = event.target.result; |
||||
|
this.objectStores.forEach(store => { |
||||
|
db.createObjectStore(store); |
||||
|
}); |
||||
|
}; |
||||
|
return new Promise((resolve, reject) => { |
||||
|
conn.onsuccess = event => { |
||||
|
resolve(new InnerDbConnection(event.target.result)); |
||||
|
}; |
||||
|
conn.onerror = event => { |
||||
|
reject(event.target.error); |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
//# sourceMappingURL=IndexedDbWrapper.js.map
|
||||
@ -0,0 +1,199 @@ |
|||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { |
||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } |
||||
|
return new (P || (P = Promise))(function (resolve, reject) { |
||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } |
||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } |
||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } |
||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next()); |
||||
|
}); |
||||
|
}; |
||||
|
var __asyncValues = (this && this.__asyncValues) || function (o) { |
||||
|
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); |
||||
|
var m = o[Symbol.asyncIterator], i; |
||||
|
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); |
||||
|
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } |
||||
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } |
||||
|
}; |
||||
|
import { IndexedDbWrapper } from "./IndexedDbWrapper"; |
||||
|
const fileBookmarksStore = "fileBookmarks"; |
||||
|
const avaloniaDb = new IndexedDbWrapper("AvaloniaDb", [ |
||||
|
fileBookmarksStore |
||||
|
]); |
||||
|
class StorageItem { |
||||
|
constructor(handle, bookmarkId) { |
||||
|
this.handle = handle; |
||||
|
this.bookmarkId = bookmarkId; |
||||
|
} |
||||
|
getName() { |
||||
|
return this.handle.name; |
||||
|
} |
||||
|
getKind() { |
||||
|
return this.handle.kind; |
||||
|
} |
||||
|
openRead() { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
if (!(this.handle instanceof FileSystemFileHandle)) { |
||||
|
throw new Error("StorageItem is not a file"); |
||||
|
} |
||||
|
yield this.verityPermissions('read'); |
||||
|
const file = yield this.handle.getFile(); |
||||
|
return file; |
||||
|
}); |
||||
|
} |
||||
|
openWrite() { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
if (!(this.handle instanceof FileSystemFileHandle)) { |
||||
|
throw new Error("StorageItem is not a file"); |
||||
|
} |
||||
|
yield this.verityPermissions('readwrite'); |
||||
|
return yield this.handle.createWritable({ keepExistingData: true }); |
||||
|
}); |
||||
|
} |
||||
|
getProperties() { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
const file = this.handle instanceof FileSystemFileHandle |
||||
|
&& (yield this.handle.getFile()); |
||||
|
if (!file) { |
||||
|
return null; |
||||
|
} |
||||
|
return { |
||||
|
Size: file.size, |
||||
|
LastModified: file.lastModified, |
||||
|
Type: file.type |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
getItems() { |
||||
|
var e_1, _a; |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
if (this.handle.kind !== "directory") { |
||||
|
return new StorageItems([]); |
||||
|
} |
||||
|
const items = []; |
||||
|
try { |
||||
|
for (var _b = __asyncValues(this.handle.entries()), _c; _c = yield _b.next(), !_c.done;) { |
||||
|
const [key, value] = _c.value; |
||||
|
items.push(new StorageItem(value)); |
||||
|
} |
||||
|
} |
||||
|
catch (e_1_1) { e_1 = { error: e_1_1 }; } |
||||
|
finally { |
||||
|
try { |
||||
|
if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b); |
||||
|
} |
||||
|
finally { if (e_1) throw e_1.error; } |
||||
|
} |
||||
|
return new StorageItems(items); |
||||
|
}); |
||||
|
} |
||||
|
verityPermissions(mode) { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
if ((yield this.handle.queryPermission({ mode })) === 'granted') { |
||||
|
return; |
||||
|
} |
||||
|
if ((yield this.handle.requestPermission({ mode })) === "denied") { |
||||
|
throw new Error("Read permissions denied"); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
saveBookmark() { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
if (this.bookmarkId) { |
||||
|
return this.bookmarkId; |
||||
|
} |
||||
|
const connection = yield avaloniaDb.connect(); |
||||
|
try { |
||||
|
const key = yield connection.put(fileBookmarksStore, this.handle, this.generateBookmarkId()); |
||||
|
return key; |
||||
|
} |
||||
|
finally { |
||||
|
connection.close(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
deleteBookmark() { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
if (!this.bookmarkId) { |
||||
|
return; |
||||
|
} |
||||
|
const connection = yield avaloniaDb.connect(); |
||||
|
try { |
||||
|
const key = yield connection.delete(fileBookmarksStore, this.bookmarkId); |
||||
|
} |
||||
|
finally { |
||||
|
connection.close(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
generateBookmarkId() { |
||||
|
return Date.now().toString(36) + Math.random().toString(36).substring(2); |
||||
|
} |
||||
|
} |
||||
|
class StorageItems { |
||||
|
constructor(items) { |
||||
|
this.items = items; |
||||
|
} |
||||
|
count() { |
||||
|
return this.items.length; |
||||
|
} |
||||
|
at(index) { |
||||
|
return this.items[index]; |
||||
|
} |
||||
|
} |
||||
|
export class StorageProvider { |
||||
|
static canOpen() { |
||||
|
return typeof window.showOpenFilePicker !== 'undefined'; |
||||
|
} |
||||
|
static canSave() { |
||||
|
return typeof window.showSaveFilePicker !== 'undefined'; |
||||
|
} |
||||
|
static canPickFolder() { |
||||
|
return typeof window.showDirectoryPicker !== 'undefined'; |
||||
|
} |
||||
|
static selectFolderDialog(startIn) { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
const options = { |
||||
|
startIn: ((startIn === null || startIn === void 0 ? void 0 : startIn.handle) || undefined) |
||||
|
}; |
||||
|
const handle = yield window.showDirectoryPicker(options); |
||||
|
return new StorageItem(handle); |
||||
|
}); |
||||
|
} |
||||
|
static openFileDialog(startIn, multiple, types, excludeAcceptAllOption) { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
const options = { |
||||
|
startIn: ((startIn === null || startIn === void 0 ? void 0 : startIn.handle) || undefined), |
||||
|
multiple, |
||||
|
excludeAcceptAllOption, |
||||
|
types: (types || undefined) |
||||
|
}; |
||||
|
const handles = yield window.showOpenFilePicker(options); |
||||
|
return new StorageItems(handles.map((handle) => new StorageItem(handle))); |
||||
|
}); |
||||
|
} |
||||
|
static saveFileDialog(startIn, suggestedName, types, excludeAcceptAllOption) { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
const options = { |
||||
|
startIn: ((startIn === null || startIn === void 0 ? void 0 : startIn.handle) || undefined), |
||||
|
suggestedName: (suggestedName || undefined), |
||||
|
excludeAcceptAllOption, |
||||
|
types: (types || undefined) |
||||
|
}; |
||||
|
const handle = yield window.showSaveFilePicker(options); |
||||
|
return new StorageItem(handle); |
||||
|
}); |
||||
|
} |
||||
|
static openBookmark(key) { |
||||
|
return __awaiter(this, void 0, void 0, function* () { |
||||
|
const connection = yield avaloniaDb.connect(); |
||||
|
try { |
||||
|
const handle = yield connection.get(fileBookmarksStore, key); |
||||
|
return handle && new StorageItem(handle, key); |
||||
|
} |
||||
|
finally { |
||||
|
connection.close(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
//# sourceMappingURL=StorageProvider.js.map
|
||||
Loading…
Reference in new issue