├── .assets ├── fig.001.png ├── movie.001.gif ├── nupkg-icon.docx └── nupkg-icon.png ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── gh-pages.yml ├── .gitignore ├── FileDropZone ├── FileDropZone.razor ├── FileDropZone.razor.cs ├── Toolbelt.Blazor.FileDropZone.csproj ├── VersionInfo.cs ├── VersionInfo.targets ├── _Imports.razor ├── bundleconfig.json ├── script.js ├── script.ts ├── tsconfig.json └── wwwroot │ └── script.min.js ├── LICENSE ├── README.md ├── RELEASE-NOTES.txt ├── SampleSite ├── App.razor ├── LICENSE ├── Layout.razor ├── Layout.razor.css ├── Program.cs ├── Properties │ └── launchSettings.json ├── SampleSite.csproj ├── UploadAvatorPicture.razor ├── UploadAvatorPicture.razor.css ├── UploadCompleted.razor ├── _Imports.razor ├── i18ntext │ ├── Layout │ │ ├── Localize.en.json │ │ └── Localize.ja.json │ ├── UploadAvatorPicture │ │ ├── Localize.en.json │ │ └── Localize.ja.json │ └── UploadCompleted │ │ ├── Localize.en.json │ │ └── Localize.ja.json └── wwwroot │ ├── favicon.ico │ ├── index.html │ └── nupkg-icon.png ├── Toolbelt.Blazor.FileDropZone.sln ├── _dist └── .gitkeep └── nuget.config /.assets/fig.001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/09e561a06a29ed158cae19850d7233763d576786/.assets/fig.001.png -------------------------------------------------------------------------------- /.assets/movie.001.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/09e561a06a29ed158cae19850d7233763d576786/.assets/movie.001.gif -------------------------------------------------------------------------------- /.assets/nupkg-icon.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/09e561a06a29ed158cae19850d7233763d576786/.assets/nupkg-icon.docx -------------------------------------------------------------------------------- /.assets/nupkg-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/09e561a06a29ed158cae19850d7233763d576786/.assets/nupkg-icon.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.csproj] 2 | indent_style = space 3 | indent_size = 2 4 | 5 | [*.cs] 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | charset = utf-8-bom 10 | 11 | # Organize usings 12 | dotnet_sort_system_directives_first = true 13 | 14 | # this. preferences 15 | dotnet_style_qualification_for_field = true:silent 16 | dotnet_style_qualification_for_property = true:silent 17 | dotnet_style_qualification_for_method = true:silent 18 | dotnet_style_qualification_for_event = true:silent 19 | 20 | # var preferences 21 | csharp_style_var_for_built_in_types = true:silent 22 | csharp_style_var_when_type_is_apparent = true:silent 23 | csharp_style_var_elsewhere = true:silent 24 | 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # Checkout the code 13 | - uses: actions/checkout@v4 14 | 15 | # Install .NET SDK 16 | - name: Setup .NET SDK 17 | uses: actions/setup-dotnet@v4 18 | with: 19 | dotnet-version: 8.0.x 20 | - name: Install .NET WebAssembly Tools 21 | run: dotnet workload install wasm-tools 22 | 23 | # Publish the site 24 | - name: Publish 25 | run: dotnet publish SampleSite/SampleSite.csproj -c:Release -o:public -p:GHPages=true 26 | 27 | # Deploy the site 28 | - name: Deploy 29 | uses: peaceiris/actions-gh-pages@v4 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | publish_dir: public/wwwroot 33 | force_orphan: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /FileDropZone/FileDropZone.razor: -------------------------------------------------------------------------------- 1 | 
2 | @ChildContent 3 |
4 | 5 | @code 6 | { 7 | [Parameter(CaptureUnmatchedValues = true)] 8 | public Dictionary? Attributes { get; set; } 9 | } -------------------------------------------------------------------------------- /FileDropZone/FileDropZone.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.JSInterop; 3 | 4 | namespace Toolbelt.Blazor.FileDropZone; 5 | 6 | public partial class FileDropZone : IAsyncDisposable 7 | { 8 | [Inject] private IJSRuntime JS { get; init; } = null!; 9 | 10 | [Parameter] public RenderFragment ChildContent { get; set; } = null!; 11 | 12 | private IJSObjectReference? _FileDropZoneHandler; 13 | 14 | private ElementReference _DropZoneElement; 15 | 16 | protected override async Task OnAfterRenderAsync(bool firstRender) 17 | { 18 | if (!firstRender) return; 19 | 20 | var scriptPath = "./_content/Toolbelt.Blazor.FileDropZone/script.min.js"; 21 | var isOnLine = await RunAsync(this.JS, js => js.InvokeAsync("Toolbelt.Blazor.getProperty", "navigator.onLine"), false); 22 | if (isOnLine) scriptPath += "?v=" + VersionInfo.VersionText; 23 | 24 | var module = await RunAsync(this.JS, js => js.InvokeAsync("import", scriptPath), default); 25 | 26 | this._FileDropZoneHandler = await RunAndDisposeAsync( 27 | module, 28 | js => js.InvokeAsync("initializeFileDropZone", this._DropZoneElement), 29 | default); 30 | } 31 | 32 | public async ValueTask DisposeAsync() 33 | { 34 | GC.SuppressFinalize(this); 35 | await RunAndDisposeAsync( 36 | this._FileDropZoneHandler, 37 | js => js.InvokeAsync("dispose"), 38 | default); 39 | } 40 | 41 | private static async ValueTask RunAsync(TJSObject? jSObject, Func> action, TResult defaultValue) 42 | { 43 | if (jSObject == null) return defaultValue; 44 | try 45 | { 46 | return await action.Invoke(jSObject); 47 | } 48 | catch (JSException) { return defaultValue; } 49 | catch (JSDisconnectedException) { return defaultValue; } 50 | } 51 | 52 | private static async ValueTask RunAndDisposeAsync(TJSObject? jSObject, Func> action, TResult defaultValue) where TJSObject : IAsyncDisposable 53 | { 54 | var result = await RunAsync(jSObject, action, defaultValue); 55 | await RunAsync(jSObject, async js => 56 | { 57 | await js.DisposeAsync(); 58 | return defaultValue; 59 | }, defaultValue); 60 | return result; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /FileDropZone/Toolbelt.Blazor.FileDropZone.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net8.0 5 | enable 6 | nullable 7 | enable 8 | bin\$(Configuration)\$(TargetFramework)\$(MSBuildProjectName).xml 9 | $(NoWarn);1591 10 | true 11 | Copyright © 2021-2024 J.Sakamoto, Mozilla Public License 2.0 12 | J.Sakamoto 13 | 3.0.1 14 | 15 | MPL-2.0 16 | https://github.com/jsakamoto/Toolbelt.Blazor.FileDropZone/ 17 | Surround an "input type=file" element by this Blazor component to making a zone that accepts drag and drops files. 18 | blazor,dragdrop,drop 19 | (Please write the package release notes in "RELEASE NOTES.txt".) 20 | nupkg-icon.png 21 | README.md 22 | ..\_dist\ 23 | true 24 | true 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | All 39 | 40 | 41 | all 42 | runtime; build; native; contentfiles; analyzers; buildtransitive 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | @(ReleaseNoteLines, '%0a');v.0.0 59 | $([System.Text.RegularExpressions.Regex]::Match($(PackageReleaseNotes), "^(v\.[\d\.]+.+?)v\.[\d\.]+", System.Text.RegularExpressions.RegexOptions.Singleline).Groups[1].Value) 60 | $(PackageReleaseNotes)%0aTo see all the change logs, please visit the following URL.%0a- https://github.com/jsakamoto/Toolbelt.Blazor.FileDropZone/blob/master/RELEASE-NOTES.txt 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /FileDropZone/VersionInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Toolbelt.Blazor.FileDropZone; 2 | internal static class VersionInfo 3 | { 4 | internal const string VersionText = "3.0.1"; 5 | } 6 | -------------------------------------------------------------------------------- /FileDropZone/VersionInfo.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(PrepareForBuildDependsOn);_GenerateVersionInfoClass 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /FileDropZone/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | -------------------------------------------------------------------------------- /FileDropZone/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputFiles": [ "script.js" ], 4 | "outputFileName": "wwwroot/script.min.js", 5 | "minify": { 6 | "enabled": true, 7 | "renameLocals": true 8 | }, 9 | "sourceMap": false 10 | } 11 | ] -------------------------------------------------------------------------------- /FileDropZone/script.js: -------------------------------------------------------------------------------- 1 | export const initializeFileDropZone = (dropZoneElement) => { 2 | const hover = 'hover'; 3 | const state = { t: -1 }; 4 | const preventDefault = (e) => e.preventDefault(); 5 | const removeHoverClass = () => dropZoneElement?.classList.remove(hover); 6 | const cancelDelay = () => { 7 | if (state.t !== -1) 8 | clearTimeout(state.t); 9 | state.t = -1; 10 | }; 11 | const onDragHover = (e) => { 12 | preventDefault(e); 13 | cancelDelay(); 14 | dropZoneElement?.classList.add(hover); 15 | }; 16 | const onDragLeave = (e) => { 17 | preventDefault(e); 18 | cancelDelay(); 19 | state.t = setTimeout(() => { 20 | state.t = -1; 21 | removeHoverClass(); 22 | }, 10); 23 | }; 24 | const dispatch = (dataTransfer) => { 25 | if (dataTransfer === null) 26 | return; 27 | if (document.contains(dropZoneElement) === false) 28 | return; 29 | const inputFileElement = dropZoneElement?.querySelector('input[type=file]'); 30 | if (inputFileElement === null) 31 | return; 32 | inputFileElement.files = dataTransfer.files; 33 | const event = new Event('change', { bubbles: true }); 34 | inputFileElement.dispatchEvent(event); 35 | }; 36 | const onDrop = (e) => { 37 | e.stopPropagation(); 38 | preventDefault(e); 39 | cancelDelay(); 40 | removeHoverClass(); 41 | dispatch(e.dataTransfer); 42 | }; 43 | const onPaste = (e) => { 44 | dispatch(e.clipboardData); 45 | }; 46 | const handlers = [ 47 | ['dragenter', onDragHover], 48 | ['dragover', onDragHover], 49 | ['dragleave', onDragLeave], 50 | ['drop', onDrop], 51 | ['paste', onPaste] 52 | ]; 53 | handlers.forEach(handler => dropZoneElement?.addEventListener(handler[0], handler[1])); 54 | return { 55 | dispose: () => { 56 | handlers.forEach(handler => dropZoneElement?.removeEventListener(handler[0], handler[1])); 57 | } 58 | }; 59 | }; 60 | -------------------------------------------------------------------------------- /FileDropZone/script.ts: -------------------------------------------------------------------------------- 1 | export const initializeFileDropZone = (dropZoneElement: HTMLElement | null): { dispose: () => void } => { 2 | 3 | const hover = 'hover'; 4 | const state = { t: -1 }; 5 | 6 | const preventDefault = (e: Event): void => e.preventDefault(); 7 | const removeHoverClass = (): void => dropZoneElement?.classList.remove(hover); 8 | 9 | const cancelDelay = (): void => { 10 | if (state.t !== -1) clearTimeout(state.t); 11 | state.t = -1; 12 | } 13 | 14 | const onDragHover = (e: DragEvent): void => { 15 | preventDefault(e); 16 | cancelDelay(); 17 | dropZoneElement?.classList.add(hover); 18 | } 19 | 20 | const onDragLeave = (e: DragEvent): void => { 21 | preventDefault(e); 22 | cancelDelay(); 23 | state.t = setTimeout(() => { 24 | state.t = -1; 25 | removeHoverClass(); 26 | }, 10); 27 | } 28 | 29 | const dispatch = (dataTransfer: DataTransfer | null): void => { 30 | if (dataTransfer === null) return; 31 | if (document.contains(dropZoneElement) === false) return; 32 | 33 | const inputFileElement = dropZoneElement?.querySelector('input[type=file]') as HTMLInputElement | null; 34 | if (inputFileElement === null) return; 35 | 36 | inputFileElement.files = dataTransfer.files; 37 | const event = new Event('change', { bubbles: true }); 38 | inputFileElement.dispatchEvent(event); 39 | } 40 | 41 | // Handle the paste and drop events 42 | const onDrop = (e: DragEvent): void => { 43 | e.stopPropagation(); 44 | preventDefault(e); 45 | cancelDelay(); 46 | removeHoverClass(); 47 | 48 | // Set the files property of the input element and raise the change event 49 | dispatch(e.dataTransfer); 50 | } 51 | 52 | const onPaste = (e: ClipboardEvent): void => { 53 | // Set the files property of the input element and raise the change event 54 | dispatch(e.clipboardData); 55 | } 56 | 57 | const handlers: ([string, (e: any) => void])[] = [ 58 | ['dragenter', onDragHover], 59 | ['dragover', onDragHover], 60 | ['dragleave', onDragLeave], 61 | ['drop', onDrop], 62 | ['paste', onPaste] 63 | ]; 64 | handlers.forEach(handler => dropZoneElement?.addEventListener(handler[0], handler[1])); 65 | 66 | return { 67 | dispose: () => { 68 | handlers.forEach(handler => dropZoneElement?.removeEventListener(handler[0], handler[1])); 69 | } 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /FileDropZone/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 5 | "module": "ES2022", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | // "lib": [], /* Specify library files to be included in the compilation. */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 12 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 13 | // "outFile": "./", /* Concatenate and emit output to single file. */ 14 | // "outDir": "./", /* Redirect output structure to the directory. */ 15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 16 | // "composite": true, /* Enable project compilation */ 17 | // "incremental": true, /* Enable incremental compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | 52 | /* Source Map Options */ 53 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 57 | 58 | /* Experimental Options */ 59 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 60 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 61 | }, 62 | "compileOnSave": true 63 | } 64 | -------------------------------------------------------------------------------- /FileDropZone/wwwroot/script.min.js: -------------------------------------------------------------------------------- 1 | export const initializeFileDropZone=n=>{const u="hover",t={t:-1},i=n=>n.preventDefault(),f=()=>n?.classList.remove(u),r=()=>{t.t!==-1&&clearTimeout(t.t),t.t=-1},e=t=>{i(t),r(),n?.classList.add(u)},h=n=>{i(n),r(),t.t=setTimeout(()=>{t.t=-1,f()},10)},o=t=>{if(t!==null&&document.contains(n)!==!1){const i=n?.querySelector("input[type=file]");if(i!==null){i.files=t.files;const r=new Event("change",{bubbles:!0});i.dispatchEvent(r)}}},c=n=>{n.stopPropagation(),i(n),r(),f(),o(n.dataTransfer)},l=n=>{o(n.clipboardData)},s=[["dragenter",e],["dragover",e],["dragleave",h],["drop",c],["paste",l]];return s.forEach(t=>n?.addEventListener(t[0],t[1])),{dispose:()=>{s.forEach(t=>n?.removeEventListener(t[0],t[1]))}}}; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blazor File Drop Zone 2 | 3 | [![NuGet Package](https://img.shields.io/nuget/v/Toolbelt.Blazor.FileDropZone.svg)](https://www.nuget.org/packages/Toolbelt.Blazor.FileDropZone/) [![Discord](https://img.shields.io/discord/798312431893348414?style=flat&logo=discord&logoColor=white&label=Blazor%20Community&labelColor=5865f2&color=gray)](https://discord.com/channels/798312431893348414/1202165955900473375) 4 | 5 | ## Summary 6 | 7 | Surround an `` element by this `` Blazor component to making a zone that accepts drag and drops files. 8 | 9 | - Live demo site - https://jsakamoto.github.io/Toolbelt.Blazor.FileDropZone/ 10 | 11 | ![fig.1](https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/master/.assets/fig.001.png) 12 | 13 | ## Usage 14 | 15 | **Step 1.** Add the NuGet package of this Blazor component to your Blazor app project. 16 | 17 | ```shell 18 | > dotnet add package Toolbelt.Blazor.FileDropZone 19 | ``` 20 | 21 | 22 | **Step 2.** Surround `` component by the `` component. 23 | 24 | Before: 25 | 26 | ```html 27 | 28 | 29 | 30 | ``` 31 | 32 | After: 33 | 34 | ```html 35 | 36 | 37 | @using Toolbelt.Blazor.FileDropZone 38 | ... 39 | 40 | 41 | 42 | 43 | 44 | ``` 45 | 46 | **Step 3.** Styling the `` component as you want to see. 47 | 48 | **[Tips]** 49 | 50 | The `` component will render just a single & plain `
` element outside of child content. 51 | 52 | That means the `` component doesn't provide any UI styles. 53 | 54 | Instead, `` the component adds/removes the `"hover"` CSS class to that `
` element when the mouse cursor enters/leaves the component area. 55 | 56 | 57 | ```css 58 | /* "Foo.razor.css" */ 59 | 60 | ::deep .drop-zone { 61 | padding: 32px; 62 | border: dashed 2px transparent; 63 | transition: border linear 0.2s; 64 | } 65 | ::deep .drop-zone.hover { 66 | border: dashed 2px darkorange; 67 | } 68 | ``` 69 | 70 | After doing the above steps, you will get a drag & drop file feature like the following image. 71 | 72 | ![movie](https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/master/.assets/movie.001.gif) 73 | 74 | When any files are dropped into the `div` element that the `` component rendered, the `` component finds a `` element from an inside of its child content. 75 | 76 | And then, the component dispatches the file object that the user dropped to the input element that the component found. 77 | 78 | ## Supported version 79 | 80 | - .NET 6 or later is required. 81 | - Both Blazor WebAssembly and Blazor Server are supported. 82 | 83 | ## Release Note 84 | 85 | [Release notes](https://github.com/jsakamoto/Toolbelt.Blazor.FileDropZone/blob/master/RELEASE-NOTES.txt) 86 | 87 | ## License 88 | 89 | [Mozilla Public License Version 2.0](https://github.com/jsakamoto/Toolbelt.Blazor.FileDropZone/blob/master/LICENSE) -------------------------------------------------------------------------------- /RELEASE-NOTES.txt: -------------------------------------------------------------------------------- 1 | v.3.0.1 2 | - Improved: Made the JavaScript interop call more robust 3 | 4 | v.3.0.0 5 | - BREAKING CHANGE: Dropped the support for the .NET 5.0 6 | - Improved: Added the efficient support for .NET 8.0 7 | - Improved: Reduced the file size of client-side script 8 | - Improved: Made the client-side script more robust 9 | - Improved: Enabled IL trimming for the Blazor WebAssembly 10 | 11 | v.2.1.1 12 | - Improved: Reduced flickering of the "hover" CSS class during drag-over. 13 | 14 | v.2.0.0 15 | - BREAKING CHANGE: stop the propagation of the "drop" event that the "FileDropZone" component handled. 16 | (to avoid an edge case about the strange behavior of web browsers.) 17 | 18 | v.1.0.1 19 | - Fix: An unhandled exception occurred when the browser window was closed on Blazor server apps. 20 | 21 | v.1.0.0 22 | - 1st release. -------------------------------------------------------------------------------- /SampleSite/App.razor: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Sorry, there's nothing at this address.

15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /SampleSite/LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /SampleSite/Layout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | @inject ISnackbar Snackbar 3 | @inject Toolbelt.Blazor.I18nText.I18nText I18nText 4 | @using SampleSite.I18nText.Layout 5 | 6 |
7 | 8 | 9 |

@Localize.Title

10 | 11 | 13 | @Localize.Source_Code 14 | 15 |
16 |
17 | 18 |
19 | @Body 20 |
21 | 22 |
23 | 24 | 28 |
29 | 30 | @code { 31 | private Localize Localize = new(); 32 | 33 | private string? CurrentLang; 34 | 35 | protected override async Task OnInitializedAsync() 36 | { 37 | var lang = await I18nText.GetCurrentLanguageAsync(); 38 | if (new[] { "en", "ja" }.Contains(lang.Split('-')[0])) 39 | { 40 | CurrentLang = lang; 41 | } 42 | else 43 | { 44 | CurrentLang = "en"; 45 | } 46 | 47 | this.Localize = await this.I18nText.GetTextTableAsync(this); 48 | this.Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter; 49 | } 50 | 51 | private async Task OnChangeCurrentLang(ChangeEventArgs args) 52 | { 53 | CurrentLang = args.Value?.ToString() ?? "en"; 54 | await I18nText.SetCurrentLanguageAsync(CurrentLang); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /SampleSite/Layout.razor.css: -------------------------------------------------------------------------------- 1 | img.package-icon { 2 | width: 62px; 3 | } 4 | 5 | ::deep h1 { 6 | font-size: 140%; 7 | } 8 | 9 | .form-area { 10 | /*border: solid 1px yellow;*/ 11 | width: 100vw; 12 | max-width: 500px; 13 | margin-top: 20px; 14 | margin-left: auto; 15 | margin-right: auto; 16 | padding: 0 30px; 17 | } 18 | 19 | .form-area ::deep .mud-paper { 20 | padding: 1px 18px; 21 | } 22 | 23 | footer { 24 | position: fixed; 25 | bottom: 0; 26 | left: 0; 27 | right: 0; 28 | padding: 10px 20px; 29 | display: flex; 30 | justify-content: flex-end; 31 | } 32 | 33 | footer select { 34 | margin-left: 7px; 35 | } 36 | -------------------------------------------------------------------------------- /SampleSite/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 2 | using MudBlazor.Services; 3 | using SampleSite; 4 | using Toolbelt.Blazor.Extensions.DependencyInjection; 5 | 6 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 7 | builder.RootComponents.Add("#app"); 8 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 9 | ConfigureServices(builder.Services); 10 | 11 | await builder.Build().RunAsync(); 12 | 13 | static void ConfigureServices(IServiceCollection services) 14 | { 15 | services.AddMudServices(); 16 | services.AddI18nText(); 17 | } 18 | -------------------------------------------------------------------------------- /SampleSite/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:57524/", 7 | "sslPort": 44382 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "SampleSite": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 23 | "applicationUrl": "http://localhost:5000/", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /SampleSite/SampleSite.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | true 7 | enable 8 | $(WarningsAsErrors);nullable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /SampleSite/UploadAvatorPicture.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using SampleSite.I18nText.UploadAvatorPicture 3 | @using Toolbelt.Blazor.FileDropZone 4 | @inject Toolbelt.Blazor.I18nText.I18nText I18nText 5 | @inject NavigationManager NavigationManager 6 | 7 |
8 | 9 | 10 | 11 | 12 |
13 | @((MarkupString)Localize.Please_upload_your_avator_picture) 14 |
15 | 16 |
17 | 18 | @if (ImageUrl != "") 19 | { 20 | 21 | } 22 | else 23 | { 24 | No Image 25 | } 26 | 27 |
28 | 29 |
30 | 31 |
48 | 49 |
50 | 51 | @Localize.Upload 52 | 53 |
54 | 55 |
56 | 57 |
58 |
59 | 60 | @code { 61 | private Localize Localize = new(); 62 | 63 | private string ImageUrl = ""; 64 | 65 | private IBrowserFile? SelectedFile = null; 66 | 67 | private string SelectedFileName => this.SelectedFile?.Name ?? this.Localize.No_file_chosen; 68 | 69 | private string? ErrorText = null; 70 | 71 | protected override async Task OnInitializedAsync() 72 | { 73 | this.Localize = await this.I18nText.GetTextTableAsync(this); 74 | } 75 | 76 | private async Task OnInputFileChange(InputFileChangeEventArgs args) 77 | { 78 | this.SelectedFile = args.File; 79 | this.ImageUrl = ""; 80 | this.ErrorText = null; 81 | 82 | if (!args.File.ContentType.StartsWith("image/")) 83 | { 84 | this.ErrorText = this.Localize.Chosed_file_is_not_picture; 85 | return; 86 | } 87 | if (args.File.Size > 512000 /*512KB*/) 88 | { 89 | this.ErrorText = this.Localize.Chosed_file_is_too_large; 90 | return; 91 | } 92 | 93 | var contentBytes = new byte[this.SelectedFile.Size]; 94 | using var stream = this.SelectedFile.OpenReadStream(); 95 | await stream.ReadAsync(contentBytes, 0, contentBytes.Length); 96 | 97 | var contentBase64Str = Convert.ToBase64String(contentBytes); 98 | var contentType = this.SelectedFile.ContentType; 99 | this.ImageUrl = "data:" + contentType + ";base64," + contentBase64Str; 100 | } 101 | 102 | private void OnClickUpload() 103 | { 104 | NavigationManager.NavigateTo("./completed"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /SampleSite/UploadAvatorPicture.razor.css: -------------------------------------------------------------------------------- 1 | ::deep .file-drop-zone { 2 | position: relative; 3 | border: dashed 2px transparent; 4 | border-radius: 6px; 5 | transition: border linear 0.2s; 6 | } 7 | 8 | ::deep .file-drop-zone.hover { 9 | border: dashed 2px var(--mud-palette-secondary); 10 | } 11 | 12 | ::deep .file-drop-zone::after { 13 | content: "Drop here"; 14 | font-size: 24px; 15 | position: absolute; 16 | top: 0; 17 | left: 0; 18 | bottom: 0; 19 | right: 0; 20 | background-color: #fff; 21 | opacity: 0; 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | transition: opacity ease 0.2s; 26 | pointer-events: none; 27 | } 28 | 29 | ::deep .file-drop-zone.hover::after { 30 | opacity: 0.8; 31 | } 32 | 33 | .avatar-picture-inputfile-container { 34 | white-space: nowrap; 35 | width: 100%; 36 | display: flex; 37 | justify-content: flex-end; 38 | align-items: baseline; 39 | } 40 | 41 | .selected-file-name { 42 | flex-grow: 1; 43 | overflow: hidden; 44 | text-overflow: ellipsis; 45 | margin-right: 10px; 46 | border-bottom: solid 1px var(--mud-palette-lines-inputs); 47 | padding: 6px 0 6px 0; 48 | } 49 | 50 | .error .caption { 51 | color: var(--mud-palette-error); 52 | } 53 | 54 | .error .selected-file-name { 55 | border-bottom-color: var(--mud-palette-error); 56 | } 57 | 58 | .error .mud-input-helper-text { 59 | color: var(--mud-palette-error); 60 | } 61 | -------------------------------------------------------------------------------- /SampleSite/UploadCompleted.razor: -------------------------------------------------------------------------------- 1 | @page "/completed" 2 | @inject Toolbelt.Blazor.I18nText.I18nText I18nText 3 | @using SampleSite.I18nText.UploadCompleted 4 | 5 |
6 | @Localize.Upload_was_completed 7 |
8 | 9 |
10 | @Localize.BACK_TO_HOME 11 |
12 | 13 | @code { 14 | private Localize Localize = new(); 15 | 16 | protected override async Task OnInitializedAsync() 17 | { 18 | this.Localize = await this.I18nText.GetTextTableAsync(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SampleSite/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using SampleSite 10 | @using MudBlazor 11 | -------------------------------------------------------------------------------- /SampleSite/i18ntext/Layout/Localize.en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Title": "\"Blazor File Drop Zone\" demonstration site", 3 | "Source_Code": "Source Code" 4 | } -------------------------------------------------------------------------------- /SampleSite/i18ntext/Layout/Localize.ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Title": "\"Blazor File Drop Zone\" デモンストレーションサイト", 3 | "Source_Code": "ソースコード" 4 | } -------------------------------------------------------------------------------- /SampleSite/i18ntext/UploadAvatorPicture/Localize.en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Please_upload_your_avator_picture": "Chose or drag & drop your avatar picture file and upload it.", 3 | "Avatar_picture_file": "Avatar picture file", 4 | "No_file_chosen": "No file chosen", 5 | "Choose_File": "Choose File", 6 | "Max_allowed_file_size_is_512KB": "Max allowed file size is 512KB.", 7 | "Upload": "Upload", 8 | "Chosed_file_is_not_picture": "Chosed file is not picture.", 9 | "Chosed_file_is_too_large": "Chosed file is too large.", 10 | "Success": "Success!" 11 | } -------------------------------------------------------------------------------- /SampleSite/i18ntext/UploadAvatorPicture/Localize.ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Please_upload_your_avator_picture": "アバター画像ファイルを選択するか、ここにドラッグ&ドロップして、アップロードしてください", 3 | "Avatar_picture_file": "アバター画像ファイル", 4 | "No_file_chosen": "ファイルが選択されていません", 5 | "Choose_File": "ファイル選択", 6 | "Max_allowed_file_size_is_512KB": "ファイルサイズは 512KB までです。", 7 | "Upload": "アップロード", 8 | "Chosed_file_is_not_picture": "画像ファイルではありません。", 9 | "Chosed_file_is_too_large": "ファイルサイズが大きすぎます。", 10 | "Success": "成功!" 11 | } -------------------------------------------------------------------------------- /SampleSite/i18ntext/UploadCompleted/Localize.en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Upload_was_completed": "Upload was completed.", 3 | "BACK_TO_HOME": "BACK TO HOME" 4 | } -------------------------------------------------------------------------------- /SampleSite/i18ntext/UploadCompleted/Localize.ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Upload_was_completed": "アップロードが完了しました。", 3 | "BACK_TO_HOME": "ホームに戻る" 4 | } -------------------------------------------------------------------------------- /SampleSite/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/09e561a06a29ed158cae19850d7233763d576786/SampleSite/wwwroot/favicon.ico -------------------------------------------------------------------------------- /SampleSite/wwwroot/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | "Blazor File Drop Zone" demonstration site 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
Loading...
21 | 22 |
23 | 24 |
25 | An unhandled error has occurred. 26 | Reload 27 | 🗙 28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /SampleSite/wwwroot/nupkg-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/09e561a06a29ed158cae19850d7233763d576786/SampleSite/wwwroot/nupkg-icon.png -------------------------------------------------------------------------------- /Toolbelt.Blazor.FileDropZone.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31717.71 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolbelt.Blazor.FileDropZone", "FileDropZone\Toolbelt.Blazor.FileDropZone.csproj", "{BF562B47-D30F-40EE-BE1A-854C625DFEBC}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleSite", "SampleSite\SampleSite.csproj", "{215D6952-419C-4776-B576-98377BC89E8E}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8B8F35DB-E33C-4086-B294-686616B19399}" 11 | ProjectSection(SolutionItems) = preProject 12 | .github\workflows\gh-pages.yml = .github\workflows\gh-pages.yml 13 | LICENSE = LICENSE 14 | README.md = README.md 15 | RELEASE-NOTES.txt = RELEASE-NOTES.txt 16 | EndProjectSection 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {BF562B47-D30F-40EE-BE1A-854C625DFEBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {BF562B47-D30F-40EE-BE1A-854C625DFEBC}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {BF562B47-D30F-40EE-BE1A-854C625DFEBC}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {BF562B47-D30F-40EE-BE1A-854C625DFEBC}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {215D6952-419C-4776-B576-98377BC89E8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {215D6952-419C-4776-B576-98377BC89E8E}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {215D6952-419C-4776-B576-98377BC89E8E}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {215D6952-419C-4776-B576-98377BC89E8E}.Release|Any CPU.Build.0 = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | GlobalSection(ExtensibilityGlobals) = postSolution 37 | SolutionGuid = {4C6F7DD6-FB11-457E-9B50-706049E54685} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /_dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/09e561a06a29ed158cae19850d7233763d576786/_dist/.gitkeep -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------